* 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.
*/
#include <cstdint>
#include "kernel_operator.h"
#include "cann_ops_blas_common.h"
#include "stbmv_tiling_data.h"
using namespace AscendC;
constexpr uint32_t BUFFER_NUM = 2;
constexpr uint32_t BYTENUM_PER_FLOAT32 = 4;
constexpr uint32_t UB_BYTENUM_PER_BLOCK = 32;
constexpr uint32_t UB_BYTENUM_PER_REPEAT = 256;
template <typename T>
class TbmvAIV {
public:
__aicore__ inline TbmvAIV() = default;
__aicore__ inline void Init(GM_ADDR aBanded, GM_ADDR x, GM_ADDR y, const TbmvTilingData& tiling);
__aicore__ inline void Process();
private:
TPipe pipe;
__aicore__ inline void CopyIn(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount);
__aicore__ inline void Compute(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount);
__aicore__ inline void CopyOut(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount);
__aicore__ inline void CopyInPad(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount);
__aicore__ inline void CopyOutPad(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount);
__aicore__ inline void ProcessFast();
__aicore__ inline void ProcessGeneralBand(uint32_t bandIdx);
__aicore__ inline void ProcessGeneralBandCol(
uint32_t bandIdx, uint32_t col, uint32_t colBatchSize, uint32_t aRowBase);
__aicore__ inline void LoadXCol(LocalTensor<T>& xLocal, uint32_t col, uint32_t count);
__aicore__ inline void CopyOutYCol(LocalTensor<T>& yLocal, uint32_t yStart, uint32_t count, uint32_t bandIdx);
__aicore__ inline uint32_t XPhysicalPos(uint32_t logical)
{
return (incx >= 0) ? (logical * absIncx) : ((n - 1U - logical) * absIncx);
}
__aicore__ inline uint32_t YPhysicalPos(uint32_t logical) { return XPhysicalPos(logical); }
GlobalTensor<T> aGM;
GlobalTensor<T> xGM;
GlobalTensor<T> yGM;
TQue<QuePosition::VECIN, BUFFER_NUM> aQueue;
TQue<QuePosition::VECIN, BUFFER_NUM> xQueue;
TQue<QuePosition::VECOUT, BUFFER_NUM> yQueue;
uint32_t vecIdx = 0;
uint32_t n = 0;
uint32_t k = 0;
uint32_t lda = 0;
uint32_t useCoreNum = 0;
uint32_t maxDataCount = 0;
int64_t incx = 1;
uint32_t absIncx = 1;
uint32_t uplo = ACLBLAS_LOWER;
uint32_t trans = ACLBLAS_OP_N;
uint32_t diag = ACLBLAS_NON_UNIT;
int elementsPerRepeat = UB_BYTENUM_PER_REPEAT / BYTENUM_PER_FLOAT32;
int elementsPerBlock = UB_BYTENUM_PER_BLOCK / BYTENUM_PER_FLOAT32;
};
template <typename T>
__aicore__ inline void TbmvAIV<T>::Init(GM_ADDR aBanded, GM_ADDR x, GM_ADDR y, const TbmvTilingData& tiling)
{
vecIdx = GetBlockIdx();
n = tiling.n;
k = tiling.k;
lda = tiling.lda;
useCoreNum = tiling.useCoreNum;
incx = tiling.incx;
uplo = tiling.uplo;
trans = tiling.trans;
diag = tiling.diag;
if (useCoreNum == 0 || useCoreNum > TBMV_MAX_CORE_NUM) {
useCoreNum = 1;
}
absIncx = (incx >= 0) ? static_cast<uint32_t>(incx) : static_cast<uint32_t>(-incx);
xGM.SetGlobalBuffer((__gm__ T*)x, (n - 1U) * absIncx + 1U);
yGM.SetGlobalBuffer((__gm__ T*)y, (n - 1U) * absIncx + 1U);
aGM.SetGlobalBuffer((__gm__ T*)aBanded, static_cast<uint64_t>(this->k + 1U) * this->lda);
maxDataCount = 30 * 1024 / BYTENUM_PER_FLOAT32;
pipe.InitBuffer(aQueue, BUFFER_NUM, maxDataCount * sizeof(T));
pipe.InitBuffer(xQueue, BUFFER_NUM, maxDataCount * sizeof(T));
pipe.InitBuffer(yQueue, BUFFER_NUM, maxDataCount * sizeof(T));
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::CopyIn(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount)
{
LocalTensor<T> LocalA = aQueue.AllocTensor<T>();
uint64_t r = static_cast<uint64_t>(rowOffset) * lda + colOffset;
DataCopy(LocalA, aGM[r], dataCount);
aQueue.EnQue<T>(LocalA);
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::Compute(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount)
{
LocalTensor<T> LocalA = aQueue.DeQue<T>();
LocalTensor<T> LocalX = xQueue.DeQue<T>();
LocalTensor<T> LocalY = yQueue.AllocTensor<T>();
int32_t eventIDMTE3ToV = static_cast<int32_t>(GetTPipePtr()->FetchEventID(AscendC::HardEvent::MTE3_V));
AscendC::SetFlag<AscendC::HardEvent::MTE3_V>(eventIDMTE3ToV);
AscendC::WaitFlag<AscendC::HardEvent::MTE3_V>(eventIDMTE3ToV);
int32_t eventIDMTE2ToV = static_cast<int32_t>(GetTPipePtr()->FetchEventID(AscendC::HardEvent::MTE2_V));
AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(eventIDMTE2ToV);
AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(eventIDMTE2ToV);
if (diag == ACLBLAS_UNIT && rowOffset == 0) {
for (uint32_t i = 0; i < dataCount; i++) {
LocalA.SetValue(i, static_cast<T>(1.0f));
}
}
Mul(LocalY, LocalA, LocalX, dataCount);
yQueue.EnQue<T>(LocalY);
aQueue.FreeTensor(LocalA);
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::CopyOut(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount)
{
LocalTensor<T> yLocal = yQueue.DeQue<T>();
if (incx == 1) {
DataCopy(yGM[colOffset + rowOffset], yLocal, dataCount);
} else {
for (uint32_t i = 0; i < dataCount; i++) {
uint32_t pos = XPhysicalPos(colOffset + rowOffset + i);
yGM.SetValue(pos, yGM.GetValue(pos) + yLocal.GetValue(i));
}
}
yQueue.FreeTensor(yLocal);
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::CopyInPad(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount)
{
uint8_t paddingNum = elementsPerBlock - dataCount % elementsPerBlock;
DataCopyExtParams copyParams{1, dataCount * BYTENUM_PER_FLOAT32, 0, 0, 0};
DataCopyPadExtParams<T> padParams{true, 0, paddingNum, 0};
uint64_t r = static_cast<uint64_t>(rowOffset) * lda + colOffset;
LocalTensor<T> LocalA = aQueue.AllocTensor<T>();
DataCopyPad(LocalA, aGM[r], copyParams, padParams);
aQueue.EnQue<T>(LocalA);
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::CopyOutPad(uint32_t rowOffset, uint32_t colOffset, uint32_t dataCount)
{
LocalTensor<T> yLocal = yQueue.DeQue<T>();
if (incx == 1) {
uint8_t paddingNum = elementsPerBlock - dataCount % elementsPerBlock;
DataCopyExtParams copyParams{1, dataCount * BYTENUM_PER_FLOAT32, 0, 0, 0};
DataCopyPadExtParams<T> padParams{true, 0, paddingNum, 0};
DataCopyPad(yGM[colOffset + rowOffset], yLocal, copyParams);
} else {
uint8_t paddingNum = elementsPerBlock - dataCount % elementsPerBlock;
DataCopyExtParams copyParams{1, dataCount * BYTENUM_PER_FLOAT32, 0, 0, 0};
DataCopyPadExtParams<T> padParams{true, 0, paddingNum, 0};
for (uint32_t i = 0; i < dataCount; i++) {
uint32_t pos = XPhysicalPos(colOffset + rowOffset + i);
yGM.SetValue(pos, yGM.GetValue(pos) + yLocal.GetValue(i));
}
}
yQueue.FreeTensor(yLocal);
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::ProcessFast()
{
for (uint32_t col = 0; col < n; col += maxDataCount) {
LocalTensor<T> xLocal = xQueue.AllocTensor<T>();
uint32_t colBatchSize = (col + maxDataCount <= n) ? maxDataCount : (n - col);
LoadXCol(xLocal, col, colBatchSize);
for (uint32_t bandIdx = vecIdx; bandIdx <= k; bandIdx += useCoreNum) {
uint32_t bandLen = n - bandIdx;
if (col >= bandLen) {
continue;
}
uint32_t dataCount = maxDataCount;
if (col + dataCount > bandLen) {
dataCount = bandLen - col;
xQueue.EnQue<T>(xLocal);
CopyInPad(bandIdx, col, dataCount);
Compute(bandIdx, col, dataCount);
CopyOutPad(bandIdx, col, dataCount);
continue;
}
xQueue.EnQue<T>(xLocal);
CopyIn(bandIdx, col, dataCount);
Compute(bandIdx, col, dataCount);
CopyOut(bandIdx, col, dataCount);
}
xQueue.FreeTensor(xLocal);
}
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::LoadXCol(LocalTensor<T>& xLocal, uint32_t col, uint32_t count)
{
if (incx == 1 && count % static_cast<uint32_t>(elementsPerBlock) == 0) {
DataCopy(xLocal, xGM[col], count);
} else if (incx == 1) {
uint8_t paddingNum = elementsPerBlock - count % elementsPerBlock;
DataCopyExtParams copyParams{1, count * BYTENUM_PER_FLOAT32, 0, 0, 0};
DataCopyPadExtParams<T> padParams{true, 0, paddingNum, 0};
DataCopyPad(xLocal, xGM[col], copyParams, padParams);
} else {
for (uint32_t i = 0; i < count; i++) {
xLocal.SetValue(i, xGM.GetValue(XPhysicalPos(col + i)));
}
}
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::CopyOutYCol(
LocalTensor<T>& yLocal, uint32_t yStart, uint32_t count, uint32_t bandIdx)
{
(void)bandIdx;
if (incx == 1 && count % static_cast<uint32_t>(elementsPerBlock) == 0) {
DataCopy(yGM[yStart], yLocal, count);
} else if (incx == 1) {
uint8_t paddingNum = elementsPerBlock - count % elementsPerBlock;
DataCopyExtParams copyParams{1, count * BYTENUM_PER_FLOAT32, 0, 0, 0};
DataCopyPadExtParams<T> padParams{true, 0, paddingNum, 0};
DataCopyPad(yGM[yStart], yLocal, copyParams);
} else {
for (uint32_t i = 0; i < count; i++) {
uint32_t pos = XPhysicalPos(yStart + i);
yGM.SetValue(pos, yGM.GetValue(pos) + yLocal.GetValue(i));
}
}
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::ProcessGeneralBandCol(
uint32_t bandIdx, uint32_t col, uint32_t colBatchSize, uint32_t aRowBase)
{
bool isLwrN = (uplo == ACLBLAS_LOWER) && (trans == ACLBLAS_OP_N);
bool isUprT = (uplo == ACLBLAS_UPPER) && ((trans == ACLBLAS_OP_T) || (trans == ACLBLAS_OP_C));
bool isLwrT = (uplo == ACLBLAS_LOWER) && ((trans == ACLBLAS_OP_T) || (trans == ACLBLAS_OP_C));
uint32_t aColBase = col;
if (isLwrT) {
aColBase = col - bandIdx;
} else if (isUprT) {
aColBase = col + bandIdx;
}
uint32_t aOffset = aRowBase * lda + aColBase;
LocalTensor<T> xLocal = xQueue.AllocTensor<T>();
LoadXCol(xLocal, col, colBatchSize);
xQueue.EnQue<T>(xLocal);
if (colBatchSize % static_cast<uint32_t>(elementsPerBlock) == 0) {
LocalTensor<T> LocalA = aQueue.AllocTensor<T>();
DataCopy(LocalA, aGM[aOffset], colBatchSize);
aQueue.EnQue<T>(LocalA);
} else {
uint8_t paddingNum = elementsPerBlock - colBatchSize % elementsPerBlock;
DataCopyExtParams copyParams{1, colBatchSize * BYTENUM_PER_FLOAT32, 0, 0, 0};
DataCopyPadExtParams<T> padParams{true, 0, paddingNum, 0};
LocalTensor<T> LocalA = aQueue.AllocTensor<T>();
DataCopyPad(LocalA, aGM[aOffset], copyParams, padParams);
aQueue.EnQue<T>(LocalA);
}
{
LocalTensor<T> LocalA = aQueue.DeQue<T>();
LocalTensor<T> LocalX = xQueue.DeQue<T>();
LocalTensor<T> LocalY = yQueue.AllocTensor<T>();
int32_t eventIDMTE3ToV = static_cast<int32_t>(GetTPipePtr()->FetchEventID(AscendC::HardEvent::MTE3_V));
AscendC::SetFlag<AscendC::HardEvent::MTE3_V>(eventIDMTE3ToV);
AscendC::WaitFlag<AscendC::HardEvent::MTE3_V>(eventIDMTE3ToV);
int32_t eventIDMTE2ToV = static_cast<int32_t>(GetTPipePtr()->FetchEventID(AscendC::HardEvent::MTE2_V));
AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(eventIDMTE2ToV);
AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(eventIDMTE2ToV);
if (diag == ACLBLAS_UNIT && bandIdx == 0) {
for (uint32_t i = 0; i < colBatchSize; i++) {
LocalA.SetValue(i, static_cast<T>(1.0f));
}
}
Mul(LocalY, LocalA, LocalX, colBatchSize);
yQueue.EnQue<T>(LocalY);
aQueue.FreeTensor(LocalA);
}
{
uint32_t yRowBaseVal = 0;
if (isLwrN || isUprT) {
yRowBaseVal = bandIdx;
}
uint32_t yStart = col + yRowBaseVal;
if (!isLwrN && !isUprT) {
yStart = col - bandIdx;
}
LocalTensor<T> yLocal = yQueue.DeQue<T>();
CopyOutYCol(yLocal, yStart, colBatchSize, bandIdx);
yQueue.FreeTensor(yLocal);
}
xQueue.FreeTensor(xLocal);
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::ProcessGeneralBand(uint32_t bandIdx)
{
uint32_t firstCol = 0;
uint32_t bandLen = n;
uint32_t aRowBase = 0;
if ((uplo == ACLBLAS_LOWER) && (trans == ACLBLAS_OP_N)) {
bandLen = n - bandIdx;
aRowBase = bandIdx;
} else if ((uplo == ACLBLAS_LOWER) && ((trans == ACLBLAS_OP_T) || (trans == ACLBLAS_OP_C))) {
firstCol = bandIdx;
aRowBase = bandIdx;
} else if ((uplo == ACLBLAS_UPPER) && (trans == ACLBLAS_OP_N)) {
firstCol = bandIdx;
aRowBase = k - bandIdx;
} else {
bandLen = n - bandIdx;
aRowBase = k - bandIdx;
}
for (uint32_t col = firstCol; col < bandLen; col += maxDataCount) {
uint32_t colBatchSize = (col + maxDataCount <= bandLen) ? maxDataCount : (bandLen - col);
if (colBatchSize == 0) {
continue;
}
ProcessGeneralBandCol(bandIdx, col, colBatchSize, aRowBase);
}
}
template <typename T>
__aicore__ inline void TbmvAIV<T>::Process()
{
SetAtomicAdd<T>();
if (vecIdx >= useCoreNum) {
SetAtomicNone();
return;
}
if ((uplo == ACLBLAS_LOWER) && (trans == ACLBLAS_OP_N)) {
ProcessFast();
} else {
for (uint32_t bandIdx = vecIdx; bandIdx <= k; bandIdx += useCoreNum) {
ProcessGeneralBand(bandIdx);
}
}
SetAtomicNone();
}
__global__ __aicore__ void stbmv_kernel(GM_ADDR aBanded, GM_ADDR x, GM_ADDR y, GM_ADDR workSpace, TbmvTilingData tiling)
{
KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
TbmvAIV<float> op;
op.Init(aBanded, x, y, tiling);
op.Process();
}
void stbmv_kernel_do(
GM_ADDR aBanded, GM_ADDR x, GM_ADDR y, GM_ADDR workSpace, const TbmvTilingData& tiling, uint32_t numBlocks,
void* stream)
{
stbmv_kernel<<<numBlocks, nullptr, stream>>>(aBanded, x, y, workSpace, tiling);
}