* 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 "acl/acl.h"
#include "kernel_operator.h"
#include "simt_api/asc_simt.h"
#include "common/helper/kernel_constant.h"
#include "common/helper/kernel_utils.h"
#include "sasum_tiling_data.h"
using namespace AscendC;
constexpr uint32_t BYTENUM_PER_FLOAT32 = 4;
constexpr uint32_t UB_BYTENUM_PER_BLOCK = 32;
constexpr uint32_t ELEMENTS_PER_BLOCK = UB_BYTENUM_PER_BLOCK / BYTENUM_PER_FLOAT32;
constexpr uint32_t REDUCE_REPEAT_BYTES = 256;
constexpr uint32_t ELEMENTS_PER_REPEAT = REDUCE_REPEAT_BYTES / BYTENUM_PER_FLOAT32;
constexpr uint32_t SAFETY_MARGIN = 32 * 1024;
constexpr uint32_t UB_MAX_CHUNK_FLOATS = 27392;
class SasumAIV {
public:
__aicore__ inline SasumAIV() {}
__aicore__ inline void Init(TPipe* pipe, GM_ADDR inGM, GM_ADDR outGM, const SasumTilingData& tdata);
__aicore__ inline void Process();
private:
__aicore__ inline void ParseTilingData(const SasumTilingData& tdata);
__aicore__ inline void CopyIn(uint32_t offset, uint32_t dataCount);
__aicore__ inline void CopyInPad(uint32_t offset, uint32_t dataCount);
__aicore__ inline void Compute(uint32_t dataCount);
__aicore__ inline void CopyOut();
__aicore__ inline void SingleIteration(uint32_t offset, uint32_t dataCount);
__aicore__ inline void SingleIterationAligned(uint32_t offset, uint32_t dataCount);
TPipe* pipe_;
TQue<QuePosition::VECIN, BUFFER_NUM> inQueue;
TQue<QuePosition::VECOUT, BUFFER_NUM> outQueue;
TBuf<TPosition::VECCALC> workBuf;
GlobalTensor<float> inGM;
GlobalTensor<float> outGM;
int32_t blockIdx;
int32_t blockNum;
uint32_t n;
uint32_t computeNum;
uint32_t startOffset;
uint32_t maxDataCount;
};
__aicore__ inline void SasumAIV::ParseTilingData(const SasumTilingData& tdata)
{
this->n = static_cast<uint32_t>(tdata.n);
this->startOffset = tdata.startOffset[this->blockIdx];
this->computeNum = tdata.calNum[this->blockIdx];
}
__aicore__ inline void SasumAIV::Init(TPipe* pipe, GM_ADDR inDevice, GM_ADDR outDevice, const SasumTilingData& tdata)
{
pipe_ = pipe;
this->blockIdx = GetBlockIdx();
this->blockNum = GetBlockNum();
ParseTilingData(tdata);
inGM.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(inDevice), this->n);
outGM.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(outDevice), 1);
maxDataCount = UB_MAX_CHUNK_FLOATS;
int elementsPerRepeat = REDUCE_REPEAT_BYTES / sizeof(float);
int level1RepeatCnt = (maxDataCount + elementsPerRepeat - 1) / elementsPerRepeat;
int level1OutputCount = level1RepeatCnt;
int level1AlignEnd = (level1OutputCount + ELEMENTS_PER_BLOCK - 1) / ELEMENTS_PER_BLOCK * ELEMENTS_PER_BLOCK;
uint32_t workBufByteLen = level1AlignEnd * sizeof(float);
pipe_->InitBuffer(workBuf, workBufByteLen + UB_BYTENUM_PER_BLOCK);
pipe_->InitBuffer(inQueue, BUFFER_NUM, maxDataCount * sizeof(float));
pipe_->InitBuffer(outQueue, BUFFER_NUM, UB_BYTENUM_PER_BLOCK);
LocalTensor<float> workLocal = workBuf.Get<float>(ELEMENTS_PER_BLOCK);
Duplicate<float>(workLocal, 0.0f, ELEMENTS_PER_BLOCK);
DataCopyParams zeroParams{1, static_cast<uint16_t>(sizeof(float)), 0, 0};
DataCopyPad(outGM, workLocal, zeroParams);
}
__aicore__ inline void SasumAIV::Process()
{
if (this->computeNum == 0) {
return;
}
SetAtomicAdd<float>();
uint32_t repeatTimes = computeNum / maxDataCount;
uint32_t remainNum = computeNum % maxDataCount;
uint32_t maxCopyPadNum = UINT16_MAX / sizeof(float) / ELEMENTS_PER_BLOCK * ELEMENTS_PER_BLOCK;
uint32_t currOffset = startOffset;
for (uint32_t i = 0; i < repeatTimes; i++) {
SingleIteration(currOffset, maxDataCount);
currOffset += maxDataCount;
}
if (remainNum > 0) {
if (remainNum >= maxCopyPadNum) {
SingleIteration(currOffset, maxCopyPadNum);
currOffset += maxCopyPadNum;
remainNum -= maxCopyPadNum;
}
if (remainNum > 0) {
SingleIterationAligned(currOffset, remainNum);
}
}
DisableDmaAtomic();
}
__aicore__ inline void SasumAIV::SingleIteration(uint32_t offset, uint32_t dataCount)
{
CopyIn(offset, dataCount);
Compute(dataCount);
CopyOut();
}
__aicore__ inline void SasumAIV::SingleIterationAligned(uint32_t offset, uint32_t dataCount)
{
uint32_t dataCountAligned = (dataCount + ELEMENTS_PER_BLOCK - 1) / ELEMENTS_PER_BLOCK * ELEMENTS_PER_BLOCK;
CopyInPad(offset, dataCount);
Compute(dataCountAligned);
CopyOut();
}
__aicore__ inline void SasumAIV::CopyIn(uint32_t offset, uint32_t dataCount)
{
LocalTensor<float> inLocal = inQueue.AllocTensor<float>();
DataCopy(inLocal, inGM[offset], dataCount);
inQueue.EnQue<float>(inLocal);
}
__aicore__ inline void SasumAIV::CopyInPad(uint32_t offset, uint32_t dataCount)
{
DataCopyParams copyParams{1, static_cast<uint16_t>(dataCount * sizeof(float)), 0, 0};
uint8_t paddingNum = ELEMENTS_PER_BLOCK - dataCount % ELEMENTS_PER_BLOCK;
DataCopyPadParams padParams{true, 0, paddingNum, 0};
LocalTensor<float> inLocal = inQueue.AllocTensor<float>();
DataCopyPad(inLocal, inGM[offset], copyParams, padParams);
inQueue.EnQue<float>(inLocal);
}
__aicore__ inline void SasumAIV::Compute(uint32_t dataCount)
{
LocalTensor<float> inLocal = inQueue.DeQue<float>();
LocalTensor<float> workLocal = workBuf.Get<float>();
LocalTensor<float> outLocal = outQueue.AllocTensor<float>();
Abs(inLocal, inLocal, dataCount);
ReduceSum(outLocal, inLocal, workLocal, dataCount);
outQueue.EnQue<float>(outLocal);
inQueue.FreeTensor(inLocal);
}
__aicore__ inline void SasumAIV::CopyOut()
{
LocalTensor<float> outLocal = outQueue.DeQue<float>();
DataCopyParams copyParams{1, static_cast<uint16_t>(sizeof(float)), 0, 0};
DataCopyPad(outGM, outLocal, copyParams);
outQueue.FreeTensor(outLocal);
}
__simt_vf__ __aicore__ LAUNCH_BOUND(SIMT_MAX_THREAD_NUM) inline void SasumSimtCompute(
uint32_t calNum, uint32_t startOffset, uint32_t stride,
__gm__ const float* xGm, __gm__ float* partialOut, uint32_t blockDimPow2)
{
if (calNum == 0) {
return;
}
__ubuf__ float ubPartialSums[SIMT_MAX_THREAD_NUM];
float partial = 0.0f;
for (uint32_t i = threadIdx.x; i < calNum; i += blockDim.x) {
float xVal = xGm[(startOffset + i) * stride];
partial += (xVal >= 0.0f) ? xVal : -xVal;
}
ubPartialSums[threadIdx.x] = partial;
asc_syncthreads();
uint32_t n = blockDimPow2;
for (uint32_t s = n >> 1; s > 0; s >>= 1) {
if (threadIdx.x < s && (threadIdx.x + s) < blockDim.x) {
ubPartialSums[threadIdx.x] += ubPartialSums[threadIdx.x + s];
}
asc_syncthreads();
}
if (threadIdx.x == 0) {
partialOut[0] = ubPartialSums[0];
}
}
class SasumReduce {
public:
__aicore__ inline void Init(TPipe* pipe, GM_ADDR inGM, GM_ADDR outGM, uint32_t count);
__aicore__ inline void Process();
private:
__aicore__ inline void CopyIn();
__aicore__ inline void Compute();
__aicore__ inline void CopyOut();
TPipe* pipe_;
TQue<QuePosition::VECIN, 1> inQueue;
TQue<QuePosition::VECOUT, 1> outQueue;
GlobalTensor<float> inGM;
GlobalTensor<float> outGM;
uint32_t count;
uint32_t paddedCount;
};
__aicore__ inline void SasumReduce::Init(TPipe* pipe, GM_ADDR inDevice, GM_ADDR outDevice, uint32_t count)
{
pipe_ = pipe;
this->count = count;
paddedCount = (count + ELEMENTS_PER_BLOCK - 1) / ELEMENTS_PER_BLOCK * ELEMENTS_PER_BLOCK;
if (paddedCount < ELEMENTS_PER_BLOCK) {
paddedCount = ELEMENTS_PER_BLOCK;
}
inGM.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(inDevice), paddedCount);
outGM.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(outDevice), 1);
pipe_->InitBuffer(inQueue, 1, paddedCount * sizeof(float));
pipe_->InitBuffer(outQueue, 1, sizeof(float));
}
__aicore__ inline void SasumReduce::Process()
{
if (count == 0) {
return;
}
CopyIn();
Compute();
CopyOut();
}
__aicore__ inline void SasumReduce::CopyIn()
{
LocalTensor<float> inLocal = inQueue.AllocTensor<float>();
if (count % ELEMENTS_PER_BLOCK != 0) {
DataCopyParams copyParams{1, static_cast<uint16_t>(count * sizeof(float)), 0, 0};
uint8_t paddingNum = static_cast<uint8_t>(paddedCount - count);
DataCopyPadParams padParams{true, 0, paddingNum, 0};
DataCopyPad(inLocal, inGM, copyParams, padParams);
} else {
DataCopy(inLocal, inGM, count);
}
inQueue.EnQue<float>(inLocal);
}
__aicore__ inline void SasumReduce::Compute()
{
LocalTensor<float> inLocal = inQueue.DeQue<float>();
LocalTensor<float> outLocal = outQueue.AllocTensor<float>();
float sum = 0.0f;
for (uint32_t i = 0; i < count; i++) {
sum += inLocal.GetValue(i);
}
outLocal.SetValue(0, sum);
outQueue.EnQue<float>(outLocal);
inQueue.FreeTensor(inLocal);
}
__aicore__ inline void SasumReduce::CopyOut()
{
LocalTensor<float> outLocal = outQueue.DeQue<float>();
DataCopyParams copyParams{1, static_cast<uint16_t>(sizeof(float)), 0, 0};
DataCopyPad(outGM, outLocal, copyParams);
outQueue.FreeTensor(outLocal);
}
__global__ __aicore__ void sasum_aiv_kernel(GM_ADDR inGM, GM_ADDR outGM, SasumTilingData tdata)
{
KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
TPipe pipe;
SasumAIV op;
op.Init(&pipe, inGM, outGM, tdata);
op.Process();
}
__global__ __aicore__ void sasum_simt_kernel(GM_ADDR inGM, GM_ADDR workSpace, SasumTilingData tdata)
{
KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
int32_t blockIdx = GetBlockIdx();
uint32_t calNum = tdata.calNum[blockIdx];
if (calNum > 0) {
asc_vf_call<SasumSimtCompute>(
dim3{tdata.nthreads, 1, 1},
calNum, tdata.startOffset[blockIdx], static_cast<uint32_t>(tdata.incx),
reinterpret_cast<__gm__ const float*>(inGM),
reinterpret_cast<__gm__ float*>(workSpace) + blockIdx,
RoundUpPow2(tdata.nthreads));
}
}
__global__ __aicore__ void sasum_reduce_kernel(GM_ADDR workSpace, GM_ADDR outGM, uint32_t count)
{
KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
TPipe pipe;
SasumReduce op;
op.Init(&pipe, workSpace, outGM, count);
op.Process();
}
void sasum_kernel_do(GM_ADDR inGM, GM_ADDR outGM, GM_ADDR workSpace,
const SasumTilingData& tiling, uint32_t numBlocks, void* stream)
{
auto aclStream = static_cast<aclrtStream>(stream);
if (tiling.incx == 1) {
sasum_aiv_kernel<<<numBlocks, nullptr, aclStream>>>(inGM, outGM, tiling);
} else {
size_t workspaceBytes = static_cast<size_t>(tiling.useCoreNum) * sizeof(float);
aclrtMemsetAsync(workSpace, workspaceBytes, 0, workspaceBytes, aclStream);
sasum_simt_kernel<<<numBlocks, nullptr, aclStream>>>(inGM, workSpace, tiling);
sasum_reduce_kernel<<<1, nullptr, aclStream>>>(workSpace, outGM, tiling.useCoreNum);
}
}