Files
TheRock/rocm-libraries/projects/hipblaslt/tensilelite/Tensile/SolutionStructs/segment_interleave.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

160 lines
8.1 KiB
Python

# Copyright Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
"""gfx1250 LDS segment-conflict interleave oracle.
Pure function of `state` that decides whether a wave-separated TDM kernel should
split its A/B halves across LDS segments (so the two MFMA read ports hit different
segments), and returns the offsets the emit sites consume.
"""
# gfx1250 LDS segment size (5 x 64 KiB segments).
SEG = 65536
def _bpe(state):
# DataType is a DataType object, not a string; numBytes() is 2.0 for bf16.
return int(state["ProblemType"]["DataType"].numBytes())
def _pad(x, blk, padElems, bpe):
if blk == 0 or padElems == 0:
return 0
return (x // blk) * (padElems * bpe)
def _data_bytes(state, tc):
numComp = state["NumWaves"] // 2
mt = state["MacroTile0"] if tc == "A" else state["MacroTile1"]
return (mt // numComp) * state["DepthU"] * _bpe(state)
def _footprint(state, tc):
d = _data_bytes(state, tc)
blk = state["LdsBlockSizePerPad%s" % tc]
padElems = state["LdsPad%s" % tc]
return d + _pad(d, blk, padElems, _bpe(state))
def _mx_scale_bases(state, mxsaStart):
"""MX scale-block LDS bases, placed after the interleaved A/B region. Returns
(ldsBaseMXSA, ldsBaseMXSB, end); a base is None when that scale is not LDS-resident."""
pt = state["ProblemType"]
hasA = bool(pt.get("MXBlockA")) and not state.get("DirectToVgprMXSA")
hasB = bool(pt.get("MXBlockB")) and not state.get("DirectToVgprMXSB")
if not (hasA or hasB):
return None, None, mxsaStart
baseA = mxsaStart
szA = int(state.get("LdsNumElementsAlignedMXSA", 0)) if hasA else 0
baseB = baseA + szA
szB = int(state.get("LdsNumElementsAlignedMXSB", 0)) if hasB else 0
return (baseA if hasA else None), (baseB if hasB else None), baseB + szB
def _coarse_vw(state):
# A must cover a full component (never crosses one). B may be narrower, as long as its column
# span (vIdxColsB below) divides compColsB evenly so B reads split on component boundaries.
numComp = state["NumWaves"] // 2
mi_threads = min(state["MatrixInstM"], state["MatrixInstN"])
coarseA = mi_threads * state["VectorWidthA"] >= state["MacroTile0"] // numComp
if not coarseA:
return False
coarseB = mi_threads * state["VectorWidthB"] >= state["MacroTile1"] // numComp
if coarseB:
return True
compColsB = state["MacroTile1"] // numComp
vIdxColsB = state["MatrixInstN"] * state.get("MatrixInstBN", 1) * state["MIWaveGroup"][1] * state["VectorWidthB"]
return vIdxColsB > 0 and compColsB % vIdxColsB == 0
def _no(reason):
return {"applicable": False, "aligned": False, "offsets": None,
"blockSpan": 0, "reason": reason, "segmentMap": ""}
def _ceil_seg(x):
return ((x + SEG - 1) // SEG) * SEG
def aligned_budget_ok(blockSpan, numLdsBlk, naturalOffsetBlk, maxLDS):
"""Return (ok, per-buffer block) for the aligned branch: the block is the next power
of two >= max(naturalOffsetBlk, blockSpan), valid only if double-buffering it fits MaxLDS."""
if numLdsBlk != 2:
return (False, None)
offsetBlk = max(naturalOffsetBlk, blockSpan)
if offsetBlk <= 0:
return (False, None)
roundup = 1 << (offsetBlk - 1).bit_length() # next power of two
if roundup * 2 > maxLDS:
return (False, None) # total = roundup + blockSpan <= roundup*2
return (True, roundup)
def evaluate(state):
pt = state["ProblemType"]
# Tri-state knob: -1 = auto (default), 0 = force baseline, 1 = force on where applicable.
# Auto takes only the no-trade-off tight branch; the LDS-growing aligned branch needs 1.
mode = state.get("LDSSegmentInterleave", -1)
if mode == 0: return _no("parameter off")
if tuple(state.get("ISA", ()))[:2] != (12, 5): return _no("not gfx1250")
if not (state.get("enableTDMA") and state.get("enableTDMB") and state["NumWaves"] > 1):
return _no("not wave-separated TDM")
if state.get("LocalSplitU", 1) > 1:
return _no("LocalSplitU>1")
if not state.get("UnrollMajorLDSA") or not state.get("UnrollMajorLDSB"):
return _no("not unrollMajor")
if state["NumWaves"] // 2 != 2: return _no("numComp!=2")
# Both write (WaveIdx//2 -> 2 comps) and read (wtid0*stride, num1DWaves=MIWaveGroup dim)
# assume exactly 2 waves per MFMA dim. MIWaveGroup!=[2,2] (e.g. [4,1]) loses the component
# jump on the dim==1 tensor and reads OOB on the dim==4 one.
if list(state.get("MIWaveGroup", [])) != [2, 2]: return _no("MIWaveGroup!=[2,2]")
if state.get("TDMSplit") or pt.get("Sparse"):
return _no("split/sparse")
# Subtile uses a separate codegen body; the emit path these offsets target runs only for
# non-subtile kernels.
if state.get("UseSubtileImpl"): return _no("subtile")
# Needs double-buffering; 1LDSBuffer==1 breaks the assumed layout. Unresolved -1 is rejected too
# (Solution.py resolves it later, then re-evaluates).
if state.get("1LDSBuffer", 0) != 0: return _no("needs 1LDSBuffer==0")
_dt = pt["DataType"]
# fp8 covers mxf8; its MX scales are relocated as a trailing block (see _mx_scale_bases).
if not (_dt.isBFloat16() or _dt.isHalf() or _dt.is8bitFloat()):
return _no("bf16/fp16/fp8 only")
if not _coarse_vw(state): return _no("fine VW")
fA, fB = _footprint(state, "A"), _footprint(state, "B")
base = state["LdsOffsetA"]
bpe = _bpe(state)
if (base % SEG) + fA + fB < SEG:
# Small MacroTile: A0,B0 fit one segment, so push component 1 to the next segment boundary
# with a segment-aligned stride. Grows LDS (Solution.py budget-checks); PGR2 double-buffer only.
if state.get("PrefetchGlobalRead") != 2: return _no("small MT: PGR!=2")
if mode == -1: return _no("auto: skip aligned (LDS growth)")
pre = _ceil_seg(base + fA + fB) - base # segment-aligned stride (== SEG for base<SEG)
offsets = {
"ldsBaseB": base + fA, # B0 right after A0 in seg0
"writeStrideBytes": pre, # segment stride; no re-pad on the jump
"readWaveStride": pre // bpe,
"footprintPacked": True,
}
# Per-buffer span: B1 ends at base + pre(=A1) + fA + fB.
blockSpan = base + pre + fA + fB
# mxf8: put the scale block after B1. Needs extra LDS, so extend the size below.
bMXSA, bMXSB, mxEnd = _mx_scale_bases(state, blockSpan)
if bMXSA is not None: offsets["ldsBaseMXSA"] = bMXSA
if bMXSB is not None: offsets["ldsBaseMXSB"] = bMXSB
blockSpan = max(blockSpan, mxEnd)
return {"applicable": True, "aligned": True, "offsets": offsets,
"blockSpan": blockSpan, "reason": "aligned",
"segmentMap": "ALIGNED seg%d={A0,B0} seg%d={A1,B1}"
% (base // SEG, (base + pre) // SEG)}
# Tight: pack [A0][B0][A1][B1] with component stride fA+fB (each footprint already includes its
# pad, so the jump is not re-padded -> A1/B1 land exactly at the previous tile's end). No LDS growth.
offsets = {
"ldsBaseB": base + fA, # B0 right after A0
"writeStrideBytes": fA + fB, # footprint stride (post-pad), no re-pad on the jump
"readWaveStride": (fA + fB) // bpe, # same, in elements
"footprintPacked": True,
}
# mxf8: put the scale block after B1. Uses no more LDS than the non-interleaved layout.
bMXSA, bMXSB, _ = _mx_scale_bases(state, base + 2 * (fA + fB))
if bMXSA is not None: offsets["ldsBaseMXSA"] = bMXSA
if bMXSB is not None: offsets["ldsBaseMXSB"] = bMXSB
a0 = base // SEG
a1 = (base + fA + fB) // SEG # tight branch guarantees a1 > a0
seg_map = "CLEAN seg%d={A0,B0} seg%d={A1,B1}" % (a0, a1)
return {"applicable": True, "aligned": False, "offsets": offsets,
"blockSpan": 0, "reason": "tight", "segmentMap": seg_map}