* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file repeat_interleave.h
* \brief
*/
#ifndef REPEAT_INTERLEAVE_H
#define REPEAT_INTERLEAVE_H
#include "op_kernel/platform_util.h"
#include "kernel_operator.h"
#include "op_kernel/math_util.h"
namespace RepeatInterleave {
using namespace AscendC;
constexpr uint64_t DOUBLE_BUFFER = 2;
constexpr uint64_t MIN_CP_THRESHOLD = 128;
template <typename T>
__simd_vf__ inline void CopyOneCpToRepeatOutVf(__ubuf__ T* xInLocalPtr, __ubuf__ T* xOutLocalPtr, int64_t dataCount,
uint16_t repeatTimes)
{
AscendC::MicroAPI::UnalignReg uIn;
AscendC::MicroAPI::UnalignReg uOut;
AscendC::MicroAPI::RegTensor<T> inputRegTensor;
AscendC::MicroAPI::DataCopyUnAlignPre(uIn, xInLocalPtr);
AscendC::MicroAPI::DataCopyUnAlign<T, AscendC::MicroAPI::PostLiteral::POST_MODE_UPDATE>(inputRegTensor, uIn,
xInLocalPtr, dataCount);
for (uint16_t i = 0; i < repeatTimes; i++) {
AscendC::MicroAPI::DataCopyUnAlign<T, AscendC::MicroAPI::PostLiteral::POST_MODE_UPDATE>(
xOutLocalPtr, inputRegTensor, uOut, dataCount);
}
AscendC::MicroAPI::DataCopyUnAlignPost(xOutLocalPtr, uOut, 0);
}
__simd_vf__ inline void CopyXToOutNormVf(__ubuf__ int8_t* xInLocalPtr, __ubuf__ int8_t* xOutLocalPtr,
uint32_t totalBytes, uint16_t size, uint16_t stride)
{
AscendC::MicroAPI::RegTensor<int8_t> inputRegTensor;
uint32_t sreg = totalBytes;
AscendC::MicroAPI::MaskReg preg;
for (uint16_t i = 0; i < size; i++) {
preg = AscendC::MicroAPI::UpdateMask<int8_t>(sreg);
AscendC::MicroAPI::AddrReg offset = AscendC::MicroAPI::CreateAddrReg<int8_t>(i, stride);
AscendC::MicroAPI::DataCopy(inputRegTensor, xInLocalPtr, offset);
AscendC::MicroAPI::DataCopy(xOutLocalPtr, inputRegTensor, offset, preg);
}
}
template <typename T, typename U>
class RepeatInterleaveImpl {
public:
__aicore__ inline RepeatInterleaveImpl(const RepeatInterleaveTilingKernelDataNorm& tilingData, TPipe& pipe)
: tilingData_(tilingData), pipe_(pipe){};
__aicore__ inline void Init(GM_ADDR x, GM_ADDR repeats, GM_ADDR y, GM_ADDR workspace);
__aicore__ inline void CopyInRepeats(int64_t repeatDimIdx);
__aicore__ inline void ComputeRepeatsOnCurDim(int64_t repeatDimIdx);
__aicore__ inline void ComputeStartOffset();
__aicore__ inline void CopyInX(int64_t repeatDimIdx, int64_t loopIdx, int64_t dataCount);
__aicore__ inline void CopyXToOut(int64_t dataCount);
__aicore__ inline void CopyOneCpToRepeatOut(const LocalTensor<T> xInLocal, uint16_t repeatTimes);
__aicore__ inline void CopyXToMatchOut(int64_t startCpIdx, int64_t cpCount);
__aicore__ inline void CopyOutY(int64_t repeatDimIdx, int64_t loopIdx, int64_t dataCount);
__aicore__ inline void CopyMatchOutToY();
__aicore__ inline void ProcessCpMatchToUb(int64_t startCpIdx, int64_t handleCpCount);
__aicore__ inline void ProcessWholeCp();
__aicore__ inline void ProcessSplitCp();
__aicore__ inline void Process();
private:
TPipe& pipe_;
AscendC::GlobalTensor<T> xGm_;
AscendC::GlobalTensor<U> repeatsGm_;
AscendC::GlobalTensor<T> yGm_;
TQue<QuePosition::VECIN, DOUBLE_BUFFER> xInQueue_;
TQue<QuePosition::VECOUT, DOUBLE_BUFFER> xOutQueue_;
TQue<QuePosition::VECIN, 1> repeatsQueue_;
const RepeatInterleaveTilingKernelDataNorm& tilingData_;
int64_t cpTileOffset_{0};
int64_t repeatsScalarValue_{0};
int64_t curCoreFinishCount_{0};
int64_t curCoreRepeatsCountOnCurDim_{0};
int64_t eachLoopHandleNum_{0};
int64_t tailLoopHandleNum_{0};
bool isRepeatsScalar_{false};
int64_t cpCountInUbFactor_{0};
int64_t copyFromXNum_{0};
int64_t copyToMatchOutNum_{0};
int64_t copyToGmNum_{0};
int64_t outStartOffset_{0};
int64_t curRepeatHeadPos_{0};
int64_t curRepeatTailPos_{0};
};
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::Init(GM_ADDR x, GM_ADDR repeats, GM_ADDR y, GM_ADDR workspace)
{
xGm_.SetGlobalBuffer((__gm__ T*)x);
repeatsGm_.SetGlobalBuffer((__gm__ U*)repeats);
yGm_.SetGlobalBuffer((__gm__ T*)y);
pipe_.InitBuffer(repeatsQueue_, 1, tilingData_.ubFactor * sizeof(U));
pipe_.InitBuffer(xInQueue_, DOUBLE_BUFFER, tilingData_.ubFactor * sizeof(T));
pipe_.InitBuffer(xOutQueue_, DOUBLE_BUFFER, tilingData_.ubFactor * sizeof(T));
cpCountInUbFactor_ = tilingData_.ubFactor / tilingData_.mergedDims[2];
if (tilingData_.repeatsCount > -1) {
isRepeatsScalar_ = true;
repeatsScalarValue_ = tilingData_.repeatsCount;
}
ComputeStartOffset();
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::CopyInRepeats(int64_t repeatDimIdx)
{
if (isRepeatsScalar_) {
return;
}
int64_t offset = repeatDimIdx % tilingData_.mergedDims[1];
int64_t dataLen = (offset + tilingData_.ubFactor) > tilingData_.mergedDims[1] ?
(tilingData_.mergedDims[1] - offset) :
tilingData_.ubFactor;
DataCopyExtParams inParams = {1, static_cast<uint32_t>(dataLen * sizeof(U)), 0, 0, 0};
DataCopyPadExtParams<U> padParams = {false, 0, 0, 0};
LocalTensor<U> repeatsLocal = repeatsQueue_.AllocTensor<U>();
DataCopyPad(repeatsLocal, repeatsGm_[offset], inParams, padParams);
repeatsQueue_.EnQue(repeatsLocal);
curRepeatHeadPos_ = offset;
curRepeatTailPos_ = offset + dataLen;
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::ComputeRepeatsOnCurDim(int64_t repeatDimIdx)
{
if (isRepeatsScalar_) {
curCoreRepeatsCountOnCurDim_ = repeatsScalarValue_;
return;
}
LocalTensor<U> repeatsLocal = repeatsQueue_.DeQue<U>();
uint32_t repeatOffset = repeatDimIdx % tilingData_.mergedDims[1] - curRepeatHeadPos_;
event_t eventIdMte2ToS = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_S));
SetFlag<HardEvent::MTE2_S>(eventIdMte2ToS);
WaitFlag<HardEvent::MTE2_S>(eventIdMte2ToS);
curCoreRepeatsCountOnCurDim_ = repeatsLocal.GetValue(repeatOffset);
repeatsQueue_.FreeTensor(repeatsLocal);
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::ComputeStartOffset()
{
int64_t batchIdx = 0;
int64_t repeatOffset = 0;
if (tilingData_.isSplitCP == 1) {
batchIdx = GetBlockIdx() / tilingData_.cpSlice;
} else {
batchIdx = GetBlockIdx() * tilingData_.eachCoreBatchCount;
}
if (isRepeatsScalar_) {
outStartOffset_ = (batchIdx * repeatsScalarValue_) * tilingData_.mergedDims[2];
} else {
outStartOffset_ = (batchIdx * tilingData_.totalRepeatSum) * tilingData_.mergedDims[2];
}
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::CopyInX(int64_t repeatDimIdx, int64_t loopIdx, int64_t dataCount)
{
int64_t repeatOffset = repeatDimIdx * tilingData_.mergedDims[2];
int64_t loopOffset = loopIdx * eachLoopHandleNum_;
int64_t offset = repeatOffset + cpTileOffset_ + loopOffset;
uint32_t dataLen = dataCount * sizeof(T);
DataCopyExtParams inParams = {1, dataLen, 0, 0, 0};
DataCopyPadExtParams<T> padParams = {false, 0, 0, 0};
LocalTensor<T> xInLocal = xInQueue_.AllocTensor<T>();
DataCopyPad(xInLocal, xGm_[offset], inParams, padParams);
xInQueue_.EnQue(xInLocal);
copyFromXNum_ = 0;
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::CopyOneCpToRepeatOut(const LocalTensor<T> xInLocal,
uint16_t repeatTimes)
{
LocalTensor<T> xOutLocal = xOutQueue_.DeQue<T>();
__ubuf__ T* xInLocalPtr = (__ubuf__ T*)xInLocal.GetPhyAddr() + copyFromXNum_;
__ubuf__ T* xOutLocalPtr = (__ubuf__ T*)xOutLocal.GetPhyAddr() + copyToMatchOutNum_;
int64_t dataCount = tilingData_.mergedDims[2];
CopyOneCpToRepeatOutVf<T>(xInLocalPtr, xOutLocalPtr, dataCount, repeatTimes);
copyToMatchOutNum_ += repeatTimes * dataCount;
xOutQueue_.EnQue(xOutLocal);
return;
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::CopyXToMatchOut(int64_t startCpIdx, int64_t cpCount)
{
LocalTensor<T> xInLocal = xInQueue_.DeQue<T>();
LocalTensor<T> xOutLocal = xOutQueue_.AllocTensor<T>();
xOutQueue_.EnQue(xOutLocal);
for (int64_t i = 0; i < cpCount; i++) {
int64_t repeatDimIdx = i + startCpIdx;
if (!isRepeatsScalar_ && ((repeatDimIdx % tilingData_.mergedDims[1]) >= curRepeatTailPos_ ||
(repeatDimIdx % tilingData_.mergedDims[1]) < curRepeatHeadPos_)) {
CopyInRepeats(repeatDimIdx);
}
ComputeRepeatsOnCurDim(repeatDimIdx);
int64_t loopSize = (curCoreRepeatsCountOnCurDim_ + cpCountInUbFactor_ - 1) / cpCountInUbFactor_;
int64_t mainRepeatTimes = cpCountInUbFactor_;
int64_t tailRepeatTimes = curCoreRepeatsCountOnCurDim_ - cpCountInUbFactor_ * (loopSize - 1);
if (curCoreRepeatsCountOnCurDim_ == 0) {
copyFromXNum_ += tilingData_.mergedDims[2];
LocalTensor<T> xOutLocal = xOutQueue_.DeQue<T>();
xOutQueue_.FreeTensor(xOutLocal);
if (likely(i < (cpCount - 1))) {
xOutLocal = xOutQueue_.AllocTensor<T>();
xOutQueue_.EnQue(xOutLocal);
}
continue;
}
for (int64_t loopIdx = 0; loopIdx < (loopSize - 1); loopIdx++) {
CopyOneCpToRepeatOut(xInLocal, mainRepeatTimes);
CopyMatchOutToY();
xOutLocal = xOutQueue_.AllocTensor<T>();
xOutQueue_.EnQue(xOutLocal);
}
CopyOneCpToRepeatOut(xInLocal, tailRepeatTimes);
CopyMatchOutToY();
if (likely(i < cpCount - 1)) {
xOutLocal = xOutQueue_.AllocTensor<T>();
xOutQueue_.EnQue(xOutLocal);
}
copyFromXNum_ += tilingData_.mergedDims[2];
}
xInQueue_.FreeTensor(xInLocal);
return;
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::CopyXToOut(int64_t dataCount)
{
LocalTensor<T> xInLocal = xInQueue_.DeQue<T>();
LocalTensor<T> xOutLocal = xOutQueue_.AllocTensor<T>();
__ubuf__ int8_t* xInLocalPtr = (__ubuf__ int8_t*)xInLocal.GetPhyAddr();
__ubuf__ int8_t* xOutLocalPtr = (__ubuf__ int8_t*)xOutLocal.GetPhyAddr();
uint32_t totalBytes = dataCount * sizeof(T);
uint16_t stride = Ops::Base::GetVRegSize();
uint16_t size = (totalBytes + stride - 1) / stride;
CopyXToOutNormVf(xInLocalPtr, xOutLocalPtr, totalBytes, size, stride);
xOutQueue_.EnQue(xOutLocal);
xInQueue_.FreeTensor(xInLocal);
return;
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::CopyMatchOutToY()
{
LocalTensor<T> xOutLocal = xOutQueue_.DeQue<T>();
DataCopyExtParams outParams;
outParams.blockCount = 1;
outParams.blockLen = copyToMatchOutNum_ * sizeof(T);
outParams.srcStride = 0;
outParams.dstStride = 0;
DataCopyPad(yGm_[outStartOffset_ + copyToGmNum_], xOutLocal, outParams);
copyToGmNum_ += copyToMatchOutNum_;
copyToMatchOutNum_ = 0;
xOutQueue_.FreeTensor(xOutLocal);
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::CopyOutY(int64_t repeatDimIdx, int64_t loopIdx, int64_t dataCount)
{
int64_t loopOffset = loopIdx * eachLoopHandleNum_;
int64_t offset = outStartOffset_ + curCoreFinishCount_ * tilingData_.mergedDims[2] + cpTileOffset_ + loopOffset;
CopyXToOut(dataCount);
LocalTensor<T> xOutLocal = xOutQueue_.DeQue<T>();
LoopModeParams loopParams;
loopParams.loop1Size = curCoreRepeatsCountOnCurDim_;
loopParams.loop2Size = 1;
loopParams.loop1SrcStride = 0;
loopParams.loop2SrcStride = 0;
loopParams.loop1DstStride = tilingData_.mergedDims[2] * sizeof(T);
loopParams.loop2DstStride = 0;
SetLoopModePara(loopParams, DataCopyMVType::UB_TO_OUT);
DataCopyExtParams outParams;
outParams.blockCount = 1;
outParams.blockLen = dataCount * sizeof(T);
outParams.srcStride = 0;
outParams.dstStride = 0;
DataCopyPad<T, PaddingMode::Compact>(yGm_[offset], xOutLocal, outParams);
ResetLoopModePara(DataCopyMVType::UB_TO_OUT);
xOutQueue_.FreeTensor(xOutLocal);
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::ProcessCpMatchToUb(int64_t startCpIdx, int64_t handleCpCount)
{
int64_t loopSize = (handleCpCount + cpCountInUbFactor_ - 1) / cpCountInUbFactor_;
int64_t mainCpNum = cpCountInUbFactor_;
int64_t tailCpNum = handleCpCount - cpCountInUbFactor_ * (loopSize - 1);
CopyInRepeats(0);
int64_t handleStartCpIdx = 0;
for (int64_t loopIdx = 0; loopIdx < (loopSize - 1); loopIdx++) {
handleStartCpIdx = startCpIdx + loopIdx * mainCpNum;
CopyInX(handleStartCpIdx, 0, mainCpNum * tilingData_.mergedDims[2]);
CopyXToMatchOut(handleStartCpIdx, mainCpNum);
}
handleStartCpIdx = startCpIdx + (loopSize - 1) * mainCpNum;
CopyInX(handleStartCpIdx, 0, tailCpNum * tilingData_.mergedDims[2]);
CopyXToMatchOut(handleStartCpIdx, tailCpNum);
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::ProcessWholeCp()
{
int64_t curCoreCpCount = tilingData_.eachCoreBatchCount;
if (GetBlockIdx() == GetBlockNum() - 1) {
curCoreCpCount = tilingData_.tailCoreBatchCount;
}
curCoreCpCount *= tilingData_.mergedDims[1];
int64_t startCpIdx = GetBlockIdx() * tilingData_.eachCoreBatchCount * tilingData_.mergedDims[1];
if (tilingData_.mergedDims[2] * sizeof(T) < MIN_CP_THRESHOLD) {
ProcessCpMatchToUb(startCpIdx, curCoreCpCount);
return;
}
int64_t loopSize = (tilingData_.mergedDims[2] + tilingData_.ubFactor - 1) / tilingData_.ubFactor;
eachLoopHandleNum_ = tilingData_.ubFactor;
tailLoopHandleNum_ = tilingData_.mergedDims[2] - tilingData_.ubFactor * (loopSize - 1);
CopyInRepeats(0);
for (int64_t repeatDimIdx = startCpIdx; repeatDimIdx < startCpIdx + curCoreCpCount; repeatDimIdx++) {
if (!isRepeatsScalar_ && ((repeatDimIdx % tilingData_.mergedDims[1]) >= curRepeatTailPos_ ||
(repeatDimIdx % tilingData_.mergedDims[1]) < curRepeatHeadPos_)) {
CopyInRepeats(repeatDimIdx);
}
ComputeRepeatsOnCurDim(repeatDimIdx);
for (int64_t loopIdx = 0; loopIdx < loopSize - 1; loopIdx++) {
CopyInX(repeatDimIdx, loopIdx, eachLoopHandleNum_);
CopyOutY(repeatDimIdx, loopIdx, eachLoopHandleNum_);
}
CopyInX(repeatDimIdx, (loopSize - 1), tailLoopHandleNum_);
CopyOutY(repeatDimIdx, (loopSize - 1), tailLoopHandleNum_);
curCoreFinishCount_ += curCoreRepeatsCountOnCurDim_;
}
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::ProcessSplitCp()
{
int64_t curCoreHandleCpTile = tilingData_.normalCP;
if (GetBlockIdx() % tilingData_.cpSlice == (tilingData_.cpSlice - 1)) {
curCoreHandleCpTile = tilingData_.tailCP;
}
int64_t loopSize = (curCoreHandleCpTile + tilingData_.ubFactor - 1) / tilingData_.ubFactor;
eachLoopHandleNum_ = tilingData_.ubFactor;
tailLoopHandleNum_ = curCoreHandleCpTile - tilingData_.ubFactor * (loopSize - 1);
cpTileOffset_ = GetBlockIdx() % tilingData_.cpSlice * tilingData_.normalCP;
int64_t startCpIdx = (GetBlockIdx() / tilingData_.cpSlice) * tilingData_.mergedDims[1];
CopyInRepeats(0);
for (int64_t repeatDimIdx = startCpIdx; repeatDimIdx < startCpIdx + tilingData_.mergedDims[1]; repeatDimIdx++) {
if (!isRepeatsScalar_ && ((repeatDimIdx % tilingData_.mergedDims[1]) >= curRepeatTailPos_ ||
(repeatDimIdx % tilingData_.mergedDims[1]) < curRepeatHeadPos_)) {
CopyInRepeats(repeatDimIdx);
}
ComputeRepeatsOnCurDim(repeatDimIdx);
for (int64_t loop = 0; loop < loopSize - 1; loop++) {
CopyInX(repeatDimIdx, loop, eachLoopHandleNum_);
CopyOutY(repeatDimIdx, loop, eachLoopHandleNum_);
}
CopyInX(repeatDimIdx, (loopSize - 1), tailLoopHandleNum_);
CopyOutY(repeatDimIdx, (loopSize - 1), tailLoopHandleNum_);
curCoreFinishCount_ += curCoreRepeatsCountOnCurDim_;
}
}
template <typename T, typename U>
__aicore__ inline void RepeatInterleaveImpl<T, U>::Process()
{
if (GetBlockIdx() >= GetBlockNum()) {
return;
}
if (tilingData_.isSplitCP == 1) {
ProcessSplitCp();
} else {
ProcessWholeCp();
}
}
}
#endif