# Copyright Advanced Micro Devices, Inc., or its affiliates. # SPDX-License-Identifier: MIT ################################################################################ # Scale GR/LR emit for MX scale factor operands (MXSA/MXSB). # # Scale factors use a simpler access pattern than data tiles: # GR: DTL with linear offset (serial * loadWidth), one buffer_load per wave # LR: ds_read_b32 per scale group (2 M-adjacent subtiles per group) # # Each function operates on a single tensor component (MXSA or MXSB), # called once per scale operand. # # Uses ti.sharedVgprGROffset / ti.sharedVgprLROffset (compat properties) # since MXScaleTilePair has gr=None, lr=None. ################################################################################ import math from rocisa.code import Module from rocisa.container import DSModifiers, MUBUFModifiers, vgpr, sgpr, mgpr from rocisa.instruction import ( BufferLoadB128, DSLoadB32, SAddCU32, SAddU32, SLShiftLeftB32, SMovB32, SMulI32, SNop, SXorB32, VAddU32, VAndB32, VMulLOU32, VReadfirstlaneB32, VXorB32, VLShiftLeftB32, VLShiftRightB32, ) # --------------------------------------------------------------------------- # Scale GR offset # --------------------------------------------------------------------------- def emitScaleGROffset(ti, writer, kernel): """Compute per-thread DTL vaddr for scale GR load.""" return Module(f"Scale GR Offset ({ti.tc})") # STUB module = Module(f"Scale GR Offset ({ti.tc})") tc = ti.tc loadWidth = ti.loadWidthGR loadWidthShift = loadWidth.bit_length() - 1 scaleGroupSize = ti.lrSubtileSize numThreadsPerGroup = (scaleGroupSize * int(ti.localSubtileGrid[1])) // loadWidth vtmp = writer.vgprPool.checkOut(1, tag="emitScaleGROffset_vtmp") stmp = writer.sgprPool.checkOut(1, tag="emitScaleGROffset_stmp") module.add(VLShiftRightB32(dst=vgpr(vtmp), shiftHex=hex(numThreadsPerGroup.bit_length()-1), src=vgpr("Serial"), comment=f"scale{tc}: groupId")) module.add(SMulI32(dst=sgpr(stmp), src0=int(ti.bpe), src1=sgpr("Strides" + tc), comment=f"scale{tc}: stride * bpe")) module.add(VMulLOU32(dst=vgpr(vtmp), src1=vgpr(vtmp), src0=sgpr(stmp), comment=f"scale{tc}: groupId * stride")) module.add(VAndB32(dst=vgpr(ti.sharedVgprGROffset[0]), src0=hex(numThreadsPerGroup - 1), src1=vgpr("Serial"), comment=f"scale{tc}: threadId")) module.add(VLShiftLeftB32(dst=vgpr(ti.sharedVgprGROffset[0]), shiftHex=hex(loadWidthShift), src=vgpr(ti.sharedVgprGROffset[0]), comment=f"scale{tc}: threadId * loadWidth")) module.add(VAddU32(dst=vgpr(ti.sharedVgprGROffset[0]), src0=vgpr(ti.sharedVgprGROffset[0]), src1=vgpr(vtmp), comment=f"scale{tc}: final offset")) writer.vgprPool.checkIn(vtmp) writer.sgprPool.checkIn(stmp) return module # --------------------------------------------------------------------------- # Scale GR load (DTL) # --------------------------------------------------------------------------- def emitScaleGRLoad(ti, writer, kernel): """Emit buffer_load_b128 DTL for scale data (global -> LDS).""" module = Module(f"Scale GR Load ({ti.tc})") tc = ti.tc isGlc = bool(kernel.get(f"NonTemporal{tc}", 0) & 0x1) isSlc = bool(kernel.get(f"NonTemporal{tc}", 0) & 0x2) isNT = bool(kernel.get(f"NonTemporal{tc}", 0) & 0x4) module.add(SMovB32(dst=mgpr(0), src=sgpr(f"LocalWriteBaseAddr{tc}"), comment=f"scale{tc}: M0 = scaleLdsBase")) mubuf = MUBUFModifiers(offen=True, offset12=0, glc=isGlc, slc=isSlc, nt=isNT, lds=True) module.add(BufferLoadB128(dst=None, vaddr=vgpr(ti.sharedVgprGROffset[0]), saddr=sgpr(f"Srd{tc}", 4), soffset=0, mubuf=mubuf, comment=f"scale{tc}: DTL b128 load")) return module # --------------------------------------------------------------------------- # Scale LR offset # --------------------------------------------------------------------------- def emitScaleLROffset(ti, writer, kernel): """Compute per-lane LDS read offset for scale LR.""" return Module(f"Scale LR Offset ({ti.tc})") # STUB module = Module(f"Scale LR Offset ({ti.tc})") tc = ti.tc wavesize = kernel["WavefrontSize"] mi = kernel["MIWaveGroup"] totalScaleBytes = (ti.macroTile // ti.waveGroupSize) * ti.scaleDepthU * int(ti.bpe) waveIdVgpr = writer.vgprPool.checkOut(1, tag="emitScaleLROffset_waveIdVgpr") module.add(VLShiftRightB32(dst=vgpr(waveIdVgpr), shiftHex=hex(wavesize.bit_length()-1), src=vgpr("Serial"), comment=f"scale{tc}: waveId")) vtmp = writer.vgprPool.checkOut(1, tag="emitScaleLROffset_vtmp") stmp = writer.sgprPool.checkOut(1, tag="emitScaleLROffset_stmp") if tc in ('A', 'MXSA'): module.add(VAndB32(dst=vgpr(vtmp), src0=mi[0]-1, src1=vgpr(waveIdVgpr), comment=f"scale{tc}: waveId %% {mi[0]}")) else: module.add(VLShiftRightB32(dst=vgpr(vtmp), shiftHex=int(math.log2(mi[0])), src=vgpr(waveIdVgpr), comment=f"scale{tc}: waveId / {mi[0]}")) module.add(SMovB32(dst=sgpr(stmp), src=totalScaleBytes, comment=f"scale{tc}: partition stride")) module.add(VMulLOU32(dst=vgpr(ti.sharedVgprLROffset[0]), src0=sgpr(stmp), src1=vgpr(vtmp), comment=f"scale{tc}: partition offset")) writer.vgprPool.checkIn(vtmp) writer.vgprPool.checkIn(waveIdVgpr) # Per-lane offset: laneId * 4 laneOffset = writer.vgprPool.checkOut(1, tag="emitScaleLROffset_laneOffset") module.add(VAndB32(dst=vgpr(laneOffset), src0=vgpr("Serial"), src1=wavesize-1, comment=f"scale{tc}: laneId")) module.add(VLShiftLeftB32(dst=vgpr(laneOffset), shiftHex=hex(2), src=vgpr(laneOffset), comment=f"scale{tc}: laneId * 4")) module.add(VAddU32(dst=vgpr(ti.sharedVgprLROffset[0]), src0=vgpr(laneOffset), src1=vgpr(ti.sharedVgprLROffset[0]), comment=f"scale{tc}: + laneOffset")) writer.vgprPool.checkIn(laneOffset) # Add global LDS offset ldsStartOffset = getattr(writer, f'ldsStartOffset{tc}', 0) if ldsStartOffset: module.add(SMovB32(dst=sgpr(stmp), src=hex(ldsStartOffset), comment=f"scale{tc}: LDS base offset")) module.add(VAddU32(dst=vgpr(ti.sharedVgprLROffset[0]), src0=vgpr(ti.sharedVgprLROffset[0]), src1=sgpr(stmp), comment=f"scale{tc}: + LDS offset")) # Init swap VGPRs module.add(SMovB32(dst=sgpr(stmp), src=writer.ldsTotalSize, comment=f"scale{tc}: ldsTotalSize")) for i in range(len(ti.sharedVgprLROffset)): vOff = ti.sharedVgprLROffset[i] vSwap = ti.sharedVgprLROffsetSwap[i] module.add(VAddU32(dst=vgpr(vSwap), src0=vgpr(vOff), src1=sgpr(stmp), comment=f"scale{tc}: swap init")) module.add(VXorB32(dst=vgpr(vSwap), src0=vgpr(vOff), src1=vgpr(vSwap), comment=f"scale{tc}: swap mask")) writer.sgprPool.checkIn(stmp) return module # --------------------------------------------------------------------------- # Scale LR load # --------------------------------------------------------------------------- def emitScaleLRLoad(ti, writer, kernel): """Emit ds_read_b32 for all scale groups.""" module = Module(f"Scale LR Load ({ti.tc})") tc = ti.tc if ti.mxBlock == 0: return module numScaleGroups = (int(ti.lrGlobalSubtileGrid[0]) // ti.waveGroupSize) * int(ti.lrGlobalSubtileGrid[1]) groupStride = int(ti.lrSubtileSize) for gid in range(numScaleGroups): dsOffset = groupStride * gid vdst = ti.vgprTiles[4 * gid].regList.indices[0] module.add(DSLoadB32(dst=vgpr(vdst), src=vgpr(ti.sharedVgprLROffset[0]), ds=DSModifiers(offset=dsOffset), comment=f"scale{tc}[group{gid}]: 4B from LDS")) return module # --------------------------------------------------------------------------- # Scale GR ptr update # --------------------------------------------------------------------------- def emitScaleGRPtrUpdate(ti, writer, kernel): """Advance scale SRD base pointer by one depthU iteration.""" module = Module() tc = ti.tc inc = int(ti.lrSubtileSize * ti.lrGlobalSubtileGrid[1]) module.addComment0("Scale SRD update: %s += %u" % (tc, inc)) module.add(SAddU32(dst=sgpr(f"Srd{tc}"), src0=sgpr(f"Srd{tc}"), src1=inc)) module.add(SAddCU32(dst=sgpr(f"Srd{tc}+1"), src0=sgpr(f"Srd{tc}+1"), src1=0)) return module # --------------------------------------------------------------------------- # Scale LDS buffer swaps # --------------------------------------------------------------------------- def emitScaleGRLDSSwap(ti, writer, kernel): """Toggle scale GR DTL write target between double-buffer halves.""" module = Module() tc = ti.tc module.addComment0("Emit code to swap %s GR m0 offsets"%tc) module.add(SXorB32(dst=sgpr(f"LocalWriteBaseAddr{tc}"), src0=sgpr(f"LocalWriteBaseAddr{tc}"), src1=sgpr(f"Swap{tc}"), comment="")) return module def emitScaleLRLDSSwap(ti, writer, kernel): """Toggle scale LR read offsets between double-buffer halves.""" module = Module() module.addComment0("Emit code to swap %s LR vgpr offsets"%ti.tc) for i in range(len(ti.sharedVgprLROffset)): vOff = ti.sharedVgprLROffset[i] vSwap = ti.sharedVgprLROffsetSwap[i] module.add(VXorB32(dst=vgpr(vOff), src0=vgpr(vOff), src1=vgpr(vSwap), comment="")) return module # ========================================================================= # Legacy Scale emit functions (moved from SubtileBasedKernel.py) # ========================================================================= ################################################## # Compute the per-thread global-read (DTL) vaddr for scale tensor tc. # # With DTL (buffer_load lds=True) the same vaddr serves as: # - global byte offset from the SRD base (where to read from global memory) # - LDS byte offset from M0 (where to write in LDS) # # Threads within a wave are split into groups of numThreadsPerGroup. # Each group loads one contiguous subtile-column worth of scale bytes: # # groupId = serial / numThreadsPerGroup (which scale column) # threadId = serial % numThreadsPerGroup (position within group) # # grOffset = groupId * stride_bpe (column byte offset via tensor stride) # + threadId * loadWidth (byte offset within column) # # Output: sharedVgprGROffset[0] = grOffset (used as vaddr in DTL load) # def _graTileAssignmentScaleSwizzledCommon(tc, writer, kernel): module = Module() module.addComment("Computing GR Offset for %s"%tc) # TODO: revisit property mappings below (lrSubtileSize, # lrGlobalSubtileGrid); add helpers on TileInfo if they recur across emit functions. ti_ = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo loadWidth = ti_.loadWidthGR loadWidthShift = loadWidth.bit_length() - 1 # lrSubtileSize = LR subtile bytes (2x2 MMA tiles = 256B for FP4 scale). # This equals the old "2 * subtileSize" (2 M-adjacent [1,2] subtiles). # lrGlobalSubtileGrid[1] = K-dim subtile count = old localSubtileGrid[1]. scaleGroupSize = ti_.lrSubtileSize numThreadsPerGroup = (scaleGroupSize * int(ti_.lrGlobalSubtileGrid[1])) // loadWidth vtmp = writer.vgprPool.checkOut(1, tag="_graTileAssignmentScaleSwizzledCommon_vtmp") stmp = writer.sgprPool.checkOut(1, tag="_graTileAssignmentScaleSwizzledCommon_stmp") module.add(VLShiftRightB32(dst=vgpr(vtmp), shiftHex=hex(int(math.log2(numThreadsPerGroup))), src=vgpr("Serial"), comment="%s: grOffset = serial / %d" % (tc, loadWidth))) module.add(SLShiftLeftB32(sgpr(stmp), int(math.log2(ti_.bpe)), sgpr("Strides%s"%tc), comment="*= bpe (%d)"%(ti_.bpe))) module.add(VMulLOU32(dst=vgpr(vtmp), src1=vgpr(vtmp), src0=sgpr(stmp), comment="Apply scale%s stride to each group"%tc)) module.add(VAndB32(dst=vgpr(ti_.sharedVgprGROffset[0]), src0=hex(numThreadsPerGroup - 1), src1=vgpr("Serial"), comment="%s: grOffset = serial %% %d" % (tc, loadWidth))) module.add(VLShiftLeftB32(dst=vgpr(ti_.sharedVgprGROffset[0]), shiftHex=hex(loadWidthShift), src=vgpr(ti_.sharedVgprGROffset[0]), comment="Scale by load width for each thread in group")) module.add(VAddU32(dst=vgpr(ti_.sharedVgprGROffset[0]), src0=vgpr(ti_.sharedVgprGROffset[0]), src1=vgpr(vtmp), comment="Final offset calc")) writer.vgprPool.checkIn(vtmp) writer.sgprPool.checkIn(stmp) return module ################################################## # Generate GR offset calculation for scaleA/B (DTL). # # With DTL, vaddr serves as both the global read offset (from SRD) # and the LDS write offset (from M0). Simple linear access: # grOffset = serial * scaleLoadWidth # def graTileAssignmentScaleSwizzled(writer, kernel): module = Module() if not kernel["ProblemType"].get("MXBlockA", 0) and not kernel["ProblemType"].get("MXBlockB", 0): return module module.add(_graTileAssignmentScaleSwizzledCommon('MXSA', writer, kernel)) module.add(_graTileAssignmentScaleSwizzledCommon('MXSB', writer, kernel)) return module ################################################## # Apply wave partition offset for scale LR. # # Each wave reads from its assigned LDS partition for scale A or B. # # MXSA: partition index = waveId % MIWaveGroup[0] (M-direction wave index) # MXSB: partition index = waveId / MIWaveGroup[0] (N-direction wave index) # Using MIWaveGroup[0] (not [1]) correctly handles asymmetric configs # (e.g. 4x1: all 4 M-waves share the same N partition -> index = 0). # # Output: sharedVgprLROffset[0] = partitionIndex * totalScaleBytes # def _applyScaleWavePartitionLROffset(module, writer, kernel, ti_, waveId): tc = ti_.tc # totalScaleBytes = bytes per wave partition in LDS for this scale tensor. # lrGlobalSubtileGrid[0] = M-dim LR subtile count (globalMMATileGrid[0] / lrSubtileShape[0]) # lrGlobalSubtileGrid[1] = K-dim LR subtile count # lrSubtileSize = bytes per LR subtile (2x2 MMA tiles for FP4 scale) index = 0 if tc == 'MXSA' else 1 totalScaleBytes = (int(ti_.lrGlobalSubtileGrid[0]) // kernel["MIWaveGroup"][index]) * int(ti_.lrGlobalSubtileGrid[1]) * int(ti_.lrSubtileSize) tmpSgpr = writer.sgprPool.checkOut(1, tag="_applyScaleWavePartitionLROffset_tmpSgpr") tmp = writer.vgprPool.checkOut(2, tag="_applyScaleWavePartitionLROffset_tmp") if tc == 'MXSA': module.add(VAndB32(dst=vgpr(tmp), src0=kernel["MIWaveGroup"][0]-1, src1=vgpr(waveId), comment="scale%s: waveId %% 2"%tc)) else: module.add(VLShiftRightB32(dst=vgpr(tmp), shiftHex=int(math.log2(kernel["MIWaveGroup"][0])), src=vgpr(waveId), comment="scale%s: waveId / numWavesM"%tc)) module.add(SMovB32(dst=sgpr(tmpSgpr), src=totalScaleBytes, comment="scale%s: scale region"%tc)) module.add(VMulLOU32(dst=vgpr(ti_.sharedVgprLROffset[0]), src0=sgpr(tmpSgpr), src1=vgpr(tmp), comment="scale%s: partition offset"%tc)) writer.vgprPool.checkIn(tmp) writer.sgprPool.checkIn(tmpSgpr) ################################################## # Generate LR offset calculation for scaleA/B. # # Computes the per-lane LDS read offset for scale tensors. Called once # during kernel setup; the resulting VGPRs are used every loop iteration. # # Final LR offset per lane: # lrOffset[lane] = wavePartitionOffset + laneId * 4 + ldsStartOffset # # where: # wavePartitionOffset = partitionIndex * totalScaleBytes # MXSA partitionIndex = waveId % MIWaveGroup[0] (M-direction) # MXSB partitionIndex = waveId / MIWaveGroup[0] (N-direction) # laneId = serial & (wavesize - 1) # ldsStartOffset = writer.ldsStartOffsetMXSA/B # # LDS layout (double-buffered, one buffer shown): # [ DataA | DataB | ScaleA | ScaleB ] # ScaleA starts at ldsStartOffsetMXSA, ScaleB at ldsStartOffsetMXSB. # # After the LR offset is fully computed, the double-buffer swap VGPR is # initialised here (not in localReadDTLInitCommonSwapVgpr, which runs # before this function and would use uninitialised values): # swapVgpr = lrOffset XOR (lrOffset + ldsTotalSize) # This lets localReadLDSBufferSwap toggle between buffer 0 and buffer 1. # def lraTileAssignmentScaleSwizzled(writer, kernel): return _lraTileAssignmentScaleSwizzled_legacy(writer, kernel) def _lraTileAssignmentScaleSwizzled_legacy(writer, kernel): module = Module() if not kernel["ProblemType"].get("MXBlockA", 0) and not kernel["ProblemType"].get("MXBlockB", 0): return module tiA_ = writer.states.mxsa.tileInfo tiB_ = writer.states.mxsb.tileInfo module.addComment0("LR Offset Calculation for Scale Tensors") wavesize = kernel["WavefrontSize"] waveIdVgpr = writer.vgprPool.checkOut(1, tag="_lraTileAssignmentScaleSwizzled_legacy_waveIdVgpr") module.add(VLShiftRightB32(dst=vgpr(waveIdVgpr), shiftHex=hex(wavesize.bit_length()-1), src=vgpr("Serial"), comment="scale: waveId")) _applyScaleWavePartitionLROffset(module, writer, kernel, tiA_, waveIdVgpr) _applyScaleWavePartitionLROffset(module, writer, kernel, tiB_, waveIdVgpr) writer.vgprPool.checkIn(waveIdVgpr) laneOffset = writer.vgprPool.checkOut(1, tag="_lraTileAssignmentScaleSwizzled_legacy_laneOffset") module.add(VAndB32(dst=vgpr(laneOffset), src0=vgpr("Serial"), src1=wavesize-1, comment="scale: laneId")) module.add(VLShiftLeftB32(dst=vgpr(laneOffset), shiftHex=hex(2), src=vgpr(laneOffset), comment="scale: laneId * 4")) module.add(VAddU32(dst=vgpr(tiA_.sharedVgprLROffset[0]), src0=vgpr(laneOffset), src1=vgpr(tiA_.sharedVgprLROffset[0]), comment="scaleA: lrOffset = laneId * 4")) module.add(VAddU32(dst=vgpr(tiB_.sharedVgprLROffset[0]), src0=vgpr(laneOffset), src1=vgpr(tiB_.sharedVgprLROffset[0]), comment="scaleB: lrOffset = laneId * 4")) writer.vgprPool.checkIn(laneOffset) tmpSgpr = writer.sgprPool.checkOut(1, tag="_lraTileAssignmentScaleSwizzled_legacy_tmpSgpr") module.add(SMovB32(dst=sgpr(tmpSgpr), src=hex(writer.ldsStartOffsetMXSA), comment="scale: LDS offset for A scale")) module.add(VAddU32(dst=vgpr(tiA_.sharedVgprLROffset[0]), src0=vgpr(tiA_.sharedVgprLROffset[0]), src1=sgpr(tmpSgpr), comment="scaleA: +=LDS offset")) module.add(SMovB32(dst=sgpr(tmpSgpr), src=hex(writer.ldsStartOffsetMXSB), comment="scale: LDS offset for B scale")) module.add(VAddU32(dst=vgpr(tiB_.sharedVgprLROffset[0]), src0=vgpr(tiB_.sharedVgprLROffset[0]), src1=sgpr(tmpSgpr), comment="scaleB: +=LDS offset")) module.add(SMovB32(dst=sgpr(tmpSgpr), src=writer.ldsTotalSize, comment="scale: total LDS size for swap")) for ti_ in [tiA_, tiB_]: for i in range(len(ti_.sharedVgprLROffset)): vgprId = ti_.sharedVgprLROffset[i] vgprSwapId = ti_.sharedVgprLROffsetSwap[i] module.add(VAddU32(dst=vgpr(vgprSwapId), src0=vgpr(vgprId), src1=sgpr(tmpSgpr), comment="scale%s: LR swap"%ti_.tc)) module.add(VXorB32(dst=vgpr(vgprSwapId), src0=vgpr(vgprId), src1=vgpr(vgprSwapId), comment="scale%s: LR swap"%ti_.tc)) writer.sgprPool.checkIn(tmpSgpr) return module ################################################## # Scale GR: Load scale bytes from global memory directly to LDS (DTL). # # Uses BufferLoadB128 with lds=True. M0 is set to scaleLdsBase, and # sharedVgprGROffset[0] = serial * scaleLoadWidth serves as both the # global read offset (from SRD) and the LDS write offset (from M0). def globalReadDoScaleSubtile(tc, writer, kernel): module = Module() if not kernel["ProblemType"].get("MXBlockA", 0) and not kernel["ProblemType"].get("MXBlockB", 0): return module tileInfo = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo isGlc = bool(kernel["NonTemporal%s"%tc] & 0x1) isSlc = bool(kernel["NonTemporal%s"%tc] & 0x2) isNT = bool(kernel["NonTemporal%s"%tc] & 0x4) assert len(tileInfo.sharedVgprGROffset) > 0, "Scale GR requires at least 1 GR offset VGPR" module.addComment0("Scale GR: %s (DTL: BufferLoadB128 -> LDS)" % tc) # Set M0 to scale LDS base address for DTL write destination module.add(SMovB32(dst=mgpr(0), src=sgpr("LocalWriteBaseAddr%s"%tc), comment="scale%s: M0 = scaleLdsBase" % tc)) # DTL load: data goes directly from global memory to LDS (no intermediate VGPR) mubuf = MUBUFModifiers(offen=True, offset12=0, glc=isGlc, slc=isSlc, nt=isNT, lds=True) module.add(BufferLoadB128(dst=None, vaddr=vgpr(tileInfo.sharedVgprGROffset[0]), saddr=sgpr("Srd%s" % tc, 4), soffset=0, mubuf=mubuf, comment="scale%s: DTL b128 load" % tc)) return module ################################################## # Scale LR: Read scale data from LDS into scale VGPRs (DSLoadB32). # # Each lane reads 4 bytes from LDS using ds_read_b32. The base address # is sharedVgprLROffset[0] (computed by lraTileAssignmentScaleSwizzled). # MMA tile and subtile selection is done via constant ds_offset at emit time. # # Each 32-bit VGPR holds 4 E8M0 scale bytes; opsel/opsel_hi selects # the correct byte per MFMA invocation. # def emitSubtileScaleDsRead(tc, writer, kernel, scaleGroupIdx): """Emit a single DSLoadB32 for a scale group (2 M-adjacent [1,2] subtiles). Each ds_read_b32 loads 4 bytes = 4 E8M0 scale values into one VGPR.""" module = Module() tileInfo = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo if tileInfo.mxBlock == 0: return module # TileInfo LR subtile (2,2) already spans 2 M-adjacent tiles -> stride = lrSubtileSize. # Legacy TileInfo subtile (1,2) spans 1 M-tile -> stride = 2 * subtileSize. if hasattr(tileInfo, 'lrSubtileSize'): groupStride = int(tileInfo.lrSubtileSize) else: groupStride = 2 * tileInfo.subtileSize dsOffset = groupStride * scaleGroupIdx vdst = tileInfo.vgprTiles[4 * scaleGroupIdx].regList.indices[0] module.add(DSLoadB32(dst=vgpr(vdst), src=vgpr(tileInfo.sharedVgprLROffset[0]), ds=DSModifiers(offset=dsOffset), comment="scale%s[group%u]: load 4B from LDS" % (tc, scaleGroupIdx))) return module def localReadDoScaleSubtile(tc, writer, kernel): """Emit scale ds_reads for all scale groups (PGR=0 path).""" module = Module() if not kernel["ProblemType"].get("MXBlockA", 0) and not kernel["ProblemType"].get("MXBlockB", 0): return module tileInfo = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo # Iterate over scale groups: one ds_read per 2 M-adjacent subtiles numScaleGroups = math.ceil(tileInfo.localSubtileGrid[0] / 2) * tileInfo.localSubtileGrid[1] for gid in range(numScaleGroups): module.add(emitSubtileScaleDsRead(tc, writer, kernel, gid)) return module ################################################## # Scale SRD pointer update: advance scale SRD by scaleDepthU * scaleBpe bytes. # def globalReadScalePtrUpdates(tc, writer, kernel): ti_ = writer.states.mxsa.tileInfo if tc == 'MXSA' else writer.states.mxsb.tileInfo return emitScaleGRPtrUpdate(ti_, writer, kernel) ################################################## # Subroutine to generate DTL M0 LDS buffer swap # # For Swizzled Scales each wave will collectively stream # the scale values # def globalReadScaleSwizzledDTLInitCommonSgpr(writer, kernel): module = Module() wavesize = kernel["WavefrontSize"] vgprWaveId = writer.vgprPool.checkOut(1, tag="globalReadScaleSwizzledDTLInitCommonSgpr_vgprWaveId") module.addComment0("Compute shared offsets used by m0 in DTL loads") module.add(VLShiftRightB32(dst=vgpr(vgprWaveId), shiftHex=hex(wavesize.bit_length()-1), src=vgpr("Serial"), comment="Wave Id")) tiMXSA_ = writer.states.mxsa.tileInfo tiMXSB_ = writer.states.mxsb.tileInfo loadWidth = tiMXSA_.loadWidthGR bytesPerLoad = loadWidth * wavesize module.add(VLShiftLeftB32(dst=vgpr(vgprWaveId), shiftHex=hex((bytesPerLoad).bit_length()-1), src=vgpr(vgprWaveId), comment="Apply wave-specific common offset (%u) for A/B"%bytesPerLoad)) module.add(SNop(waitState=0, comment="Wait for VGPR to be ready")) module.add(VReadfirstlaneB32(dst=sgpr("LocalWriteBaseAddrMXSA"), src=vgpr(vgprWaveId), comment="Store base LDS offset, will be modified")) module.add(VReadfirstlaneB32(dst=sgpr("LocalWriteBaseAddrMXSB"), src=vgpr(vgprWaveId), comment="Store base LDS offset, will be modified")) module.add(SAddU32(dst=sgpr("LocalWriteBaseAddrMXSA"), src0=sgpr("LocalWriteBaseAddrMXSA"), src1=hex(writer.ldsStartOffsetMXSA), comment="")) module.add(SAddU32(dst=sgpr("LocalWriteBaseAddrMXSB"), src0=sgpr("LocalWriteBaseAddrMXSB"), src1=hex(writer.ldsStartOffsetMXSB), comment="")) module.add(SAddU32(dst=sgpr("SwapMXSA"), src0=sgpr("LocalWriteBaseAddrMXSA"), src1=writer.ldsTotalSize, comment="")) module.add(SXorB32(dst=sgpr("SwapMXSA"), src0=sgpr("LocalWriteBaseAddrMXSA"), src1=sgpr("SwapMXSA"), comment="")) module.add(SAddU32(dst=sgpr("SwapMXSB"), src0=sgpr("LocalWriteBaseAddrMXSB"), src1=writer.ldsTotalSize, comment="")) module.add(SXorB32(dst=sgpr("SwapMXSB"), src0=sgpr("LocalWriteBaseAddrMXSB"), src1=sgpr("SwapMXSB"), comment="")) writer.vgprPool.checkIn(vgprWaveId) return module