* 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 mod.h
* \brief Mod operator implementation
*/
#ifndef MOD_H
#define MOD_H
#include "kernel_operator.h"
#include "mod_tiling_data.h"
#include "mod_tiling_key.h"
#include <limits>
#if (defined(__NPU_ARCH__) && __NPU_ARCH__ == 2201) || (defined(__CCE_AICORE__) && __CCE_AICORE__ == 220) || \
defined(__DAV_C220_VEC__)
#define MOD_ENH_ARCH22 1
#include "adv_api/math/sign.h"
#else
#define MOD_ENH_ARCH22 0
#endif
namespace ModNs {
using namespace AscendC;
template <typename T, typename ST = T, typename OT = T>
class Mod {
public:
__aicore__ inline Mod(){};
__aicore__ inline void Init(GM_ADDR x1, GM_ADDR x2, GM_ADDR y, GM_ADDR workspace, const ModTilingData* tilingData);
__aicore__ inline void Process();
uint8_t bufferNum = 2;
constexpr static uint8_t DATA_BLOCK = 64;
constexpr static int QUEUE_DEPTH = 2;
static constexpr bool NEED_FP32_IO_BUF = std::is_same_v<T, half> || std::is_same_v<T, bfloat16_t> ||
std::is_same_v<T, int16_t>;
static constexpr bool USE_ALGO_A = !std::is_same_v<T, int16_t>;
static constexpr bool USE_LEAN_CONTIG = std::is_same_v<T, float> || std::is_same_v<T, half> ||
std::is_same_v<T, bfloat16_t>;
private:
__aicore__ inline void CopyIn(uint64_t offset, int32_t calCount, bool isConstantX2 = false);
__aicore__ inline void CopyOut(uint64_t offset, int32_t calCount);
__aicore__ inline void ParseTilingData(const ModTilingData* tilingData);
__aicore__ inline void InitConstants();
__aicore__ inline void ProcessBroadcast(uint64_t inOffset, uint64_t outOffset);
__aicore__ inline void ProcessContiguous(uint64_t inOffset, uint64_t outOffset);
__aicore__ inline void Compute(int32_t computeCount);
__aicore__ inline uint64_t GetInput2Offset(uint64_t outputOffset);
__aicore__ inline uint32_t GetInput2ContiguousCopyCount(uint64_t outputOffset, uint32_t remainingCount,
bool& isConstantRun);
__aicore__ inline void InitBuffers();
__aicore__ inline void ComputeCore(const int32_t calCount, LocalTensor<ST>& x1Tensor, LocalTensor<OT>& x2Tensor,
LocalTensor<T>& dstTensor, LocalTensor<uint8_t>& sharedTmpBuffer);
#if MOD_ENH_ARCH22
static constexpr int32_t FLAT_SLOTS = 2;
__aicore__ inline void InitFlatBuffers();
template <bool LEAN>
__aicore__ inline void ProcessContigPipeline(uint64_t inOffset, uint64_t outOffset);
__aicore__ inline void CopyInFlat(int32_t slot, uint64_t offset, uint64_t n, bool reuse);
__aicore__ inline void CopyOutFlat(int32_t slot, uint64_t offset, uint64_t n);
__aicore__ inline void LoadScalarOtherFlat();
__aicore__ inline LocalTensor<ST> FlatSelfSlot(int32_t slot);
__aicore__ inline LocalTensor<OT> FlatOtherSlot(int32_t slot);
__aicore__ inline LocalTensor<T> FlatOutSlot(int32_t slot);
__aicore__ inline void InitFusedBcastBuffers();
__aicore__ inline void ProcessFusedBcast();
__aicore__ inline void BuildOuterOtherFused();
__aicore__ inline void CopyInFusedBcast(uint64_t rowOffset, uint64_t rows);
__aicore__ inline void ComputeFusedBcast(uint64_t rows);
__aicore__ inline void CopyOutFusedBcast(uint64_t rowOffset, uint64_t rows);
__aicore__ inline void InitLeanWorkBuffers();
__aicore__ inline void ComputeContigLean(int32_t calCount, LocalTensor<ST>& x1Tensor, LocalTensor<OT>& x2Tensor,
LocalTensor<T>& dstTensor);
#endif
__aicore__ inline void ComputeInt32(const int32_t calCount, const int32_t alignedCalCount,
LocalTensor<T>& dstTensor, LocalTensor<ST>& x1Tensor, LocalTensor<OT>& x2Tensor,
LocalTensor<uint8_t>& sharedTmpBuffer);
__aicore__ inline void ComputeFPCore(const int32_t calCount, const int32_t alignedCalCount,
LocalTensor<float>& x1Float, LocalTensor<float>& x2Float,
LocalTensor<float>& resRem, LocalTensor<float>& resQuot,
LocalTensor<uint8_t>& sharedTmpBuffer);
template <typename T1, typename T2>
__aicore__ inline T1 CeilAlign(T1 a, T2 b);
__aicore__ inline void TwoProd(const LocalTensor<float>& pOut, const LocalTensor<float>& eOut,
const LocalTensor<float>& x, const LocalTensor<float>& y,
const LocalTensor<float>& s0, const LocalTensor<float>& s1,
const LocalTensor<float>& s2, int32_t cnt);
#if MOD_ENH_ARCH22
__aicore__ inline void RemainderAlgoA(const LocalTensor<float>& rOut, const LocalTensor<float>& aIn,
const LocalTensor<float>& bIn, const LocalTensor<float>& w0,
const LocalTensor<float>& w1, const LocalTensor<float>& w2,
const LocalTensor<float>& w3, const LocalTensor<float>& w4,
const LocalTensor<float>& w5, int32_t cnt);
__aicore__ inline void RemainderNaive(const LocalTensor<float>& rOut, const LocalTensor<float>& aIn,
const LocalTensor<float>& bIn, const LocalTensor<float>& work, int32_t cnt);
__aicore__ inline void RemainderAdaptive(const LocalTensor<float>& rOut, const LocalTensor<float>& aIn,
const LocalTensor<float>& bIn, const LocalTensor<float>& w0,
const LocalTensor<float>& w1, const LocalTensor<float>& w2,
const LocalTensor<float>& w3, const LocalTensor<float>& w4,
const LocalTensor<float>& w5, int32_t cnt);
__aicore__ inline void RemainderRouted(const LocalTensor<float>& rOut, const LocalTensor<float>& aIn,
const LocalTensor<float>& bIn, const LocalTensor<float>& w0,
const LocalTensor<float>& w1, const LocalTensor<float>& w2,
const LocalTensor<float>& w3, const LocalTensor<float>& w4,
const LocalTensor<float>& w5, int32_t cnt);
__aicore__ inline void PreScanCoreRoute();
#endif
private:
TPipe pipe;
TQue<QuePosition::VECIN, QUEUE_DEPTH> inputx1Queue;
TQue<QuePosition::VECIN, QUEUE_DEPTH> inputx2Queue;
TQue<QuePosition::VECOUT, QUEUE_DEPTH> outputQueue;
#if MOD_ENH_ARCH22
TBuf<TPosition::VECCALC> flatSelfBuf_;
TBuf<TPosition::VECCALC> flatOtherBuf_;
TBuf<TPosition::VECCALC> flatOtherScalarBuf_;
TBuf<TPosition::VECCALC> flatOutBuf_;
event_t flatEvMte2V_[FLAT_SLOTS] = {EVENT_ID0, EVENT_ID0};
event_t flatEvVMte2_[FLAT_SLOTS] = {EVENT_ID0, EVENT_ID0};
event_t flatEvVMte3_[FLAT_SLOTS] = {EVENT_ID0, EVENT_ID0};
event_t flatEvMte3V_[FLAT_SLOTS] = {EVENT_ID0, EVENT_ID0};
TBuf<TPosition::VECCALC> bcastOtherBuf_;
#endif
TBuf<TPosition::VECCALC> tmpBuff;
TBuf<TPosition::VECCALC> ResQuotTensorBuff;
TBuf<TPosition::VECCALC> ResRemTensorBuff;
TBuf<TPosition::VECCALC> A1Buff;
TBuf<TPosition::VECCALC> A2Buff;
TBuf<TPosition::VECCALC> A3Buff;
TBuf<TPosition::VECCALC> A4Buff;
TBuf<TPosition::VECCALC> A5Buff;
TBuf<TPosition::VECCALC> ZeroTensorBuff;
TBuf<TPosition::VECCALC> InfTensorBuff;
TBuf<TPosition::VECCALC> NanTensorBuff;
TBuf<TPosition::VECCALC> MaskTensorBuff;
TBuf<TPosition::VECCALC> EpsilonTensorBuff;
TBuf<TPosition::VECCALC> x1TensorFP32Buff;
TBuf<TPosition::VECCALC> x2TensorFP32Buff;
TBuf<TPosition::VECCALC> FP32MaxValidBuff;
TBuf<TPosition::VECCALC> INT32MaxValidBuff;
TBuf<TPosition::VECCALC> SplitQuotInt32Buff;
TBuf<TPosition::VECCALC> SplitRemInt32Buff;
LocalTensor<float> ResQuotTensor;
LocalTensor<float> ResRemTensor;
LocalTensor<float> ZeroTensor;
LocalTensor<float> InfTensor;
LocalTensor<float> NanTensor;
LocalTensor<uint8_t> MaskTensor;
LocalTensor<float> EpsilonTensor;
LocalTensor<float> x1TensorFP32Tensor;
LocalTensor<float> x2TensorFP32Tensor;
LocalTensor<float> FP32MaxValidTensor;
LocalTensor<int32_t> INT32MaxValidTensor;
LocalTensor<int32_t> SplitQuotInt32Tensor;
LocalTensor<int32_t> SplitRemInt32Tensor;
GlobalTensor<ST> inputx1GM;
GlobalTensor<OT> inputx2GM;
GlobalTensor<T> outputGM;
uint32_t coreNum = 0;
uint64_t tailCoreNum = 0;
uint64_t perCoreDataCount = 0;
uint64_t blockOffset = 0;
uint32_t blockIdx = 0;
uint32_t maxDataCount = 0;
uint32_t actualMaxDataCount = 0;
uint32_t usableUbSize = 0;
bool isInput2Scalar = false;
bool isInput2SameShape = false;
uint32_t dimNum = 0;
uint64_t input1Shape[8] = {1, 1, 1, 1, 1, 1, 1, 1};
uint64_t input2Shape[8] = {1, 1, 1, 1, 1, 1, 1, 1};
uint64_t input2Stride[8] = {0, 0, 0, 0, 0, 0, 0, 0};
float naiveThresh_ = 256.0f;
uint32_t coreRoute_ = 0;
uint32_t bcastFusedMode_ = 0;
uint64_t bcOuter_ = 0;
uint64_t bcInner_ = 0;
uint64_t bcUbFormer_ = 0;
uint64_t bcBlockFactor_ = 0;
uint64_t bcIpad_ = 0;
uint64_t coreRowBase_ = 0;
uint64_t coreRows_ = 0;
int32_t ShiftParam = 24;
float infValue = std::numeric_limits<float>::infinity();
float nanValue = std::numeric_limits<float>::quiet_NaN();
float Zero = float(0);
float Epsilon = 1e-20f;
float FP32MaxValid = 16777216.0f;
int32_t INT32MaxValid = 16777216;
};
#include "mod_algoa_impl.h"
template <typename T, typename ST, typename OT>
__aicore__ inline void Mod<T, ST, OT>::InitBuffers()
{
#if MOD_ENH_ARCH22
if (bcastFusedMode_ != 0) {
InitFusedBcastBuffers();
return;
} else if (isInput2Scalar || isInput2SameShape) {
if constexpr (USE_LEAN_CONTIG) {
InitFlatBuffers();
InitLeanWorkBuffers();
return;
} else {
InitFlatBuffers();
}
} else {
pipe.InitBuffer(inputx1Queue, bufferNum, actualMaxDataCount * sizeof(ST));
pipe.InitBuffer(inputx2Queue, bufferNum, actualMaxDataCount * sizeof(OT));
pipe.InitBuffer(outputQueue, bufferNum, actualMaxDataCount * sizeof(T));
}
#else
pipe.InitBuffer(inputx1Queue, bufferNum, actualMaxDataCount * sizeof(ST));
pipe.InitBuffer(inputx2Queue, bufferNum, actualMaxDataCount * sizeof(OT));
pipe.InitBuffer(outputQueue, bufferNum, actualMaxDataCount * sizeof(T));
#endif
pipe.InitBuffer(tmpBuff, maxDataCount * sizeof(float));
if constexpr (!std::is_same_v<T, int>) {
pipe.InitBuffer(ResQuotTensorBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(ResRemTensorBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(ZeroTensorBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(InfTensorBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(NanTensorBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(MaskTensorBuff, maxDataCount * sizeof(uint8_t));
#if MOD_ENH_ARCH22
if constexpr (USE_ALGO_A) {
pipe.InitBuffer(A1Buff, maxDataCount * sizeof(float));
pipe.InitBuffer(A2Buff, maxDataCount * sizeof(float));
pipe.InitBuffer(A3Buff, maxDataCount * sizeof(float));
pipe.InitBuffer(A4Buff, maxDataCount * sizeof(float));
pipe.InitBuffer(A5Buff, maxDataCount * sizeof(float));
}
#endif
if constexpr (NEED_FP32_IO_BUF) {
pipe.InitBuffer(x1TensorFP32Buff, maxDataCount * sizeof(float));
pipe.InitBuffer(x2TensorFP32Buff, maxDataCount * sizeof(float));
}
}
if constexpr (std::is_same_v<T, int>) {
pipe.InitBuffer(x1TensorFP32Buff, maxDataCount * sizeof(float));
pipe.InitBuffer(x2TensorFP32Buff, maxDataCount * sizeof(float));
pipe.InitBuffer(ResQuotTensorBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(ResRemTensorBuff, maxDataCount * sizeof(float));
#if defined(HIGH_PRECISION) && HIGH_PRECISION == 1
pipe.InitBuffer(FP32MaxValidBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(INT32MaxValidBuff, maxDataCount * sizeof(int32_t));
pipe.InitBuffer(SplitQuotInt32Buff, maxDataCount * sizeof(int32_t));
pipe.InitBuffer(SplitRemInt32Buff, maxDataCount * sizeof(int32_t));
pipe.InitBuffer(EpsilonTensorBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(ZeroTensorBuff, maxDataCount * sizeof(float));
pipe.InitBuffer(MaskTensorBuff, maxDataCount * sizeof(uint8_t));
#endif
}
}
#include "mod_flat_impl.h"
template <typename T, typename ST, typename OT>
__aicore__ inline void Mod<T, ST, OT>::Init(GM_ADDR x1, GM_ADDR x2, GM_ADDR y, GM_ADDR workspace,
const ModTilingData* tilingData)
{
inputx1GM.SetGlobalBuffer((__gm__ ST*)x1);
inputx2GM.SetGlobalBuffer((__gm__ OT*)x2);
outputGM.SetGlobalBuffer((__gm__ T*)y);
ParseTilingData(tilingData);
uint32_t targetMaxData = tilingData->usableUbSize;
if (perCoreDataCount < targetMaxData) {
maxDataCount = perCoreDataCount;
bufferNum = 1;
} else {
maxDataCount = targetMaxData;
bufferNum = 2;
}
if (maxDataCount < DATA_BLOCK) {
maxDataCount = DATA_BLOCK;
}
maxDataCount = (maxDataCount + DATA_BLOCK - 1) / DATA_BLOCK * DATA_BLOCK;
actualMaxDataCount = maxDataCount;
#if MOD_ENH_ARCH22
if (bcastFusedMode_ != 0) {
uint64_t tileElems = bcUbFormer_ * bcIpad_;
if (tileElems < DATA_BLOCK) {
tileElems = DATA_BLOCK;
}
maxDataCount = static_cast<uint32_t>((tileElems + DATA_BLOCK - 1) / DATA_BLOCK * DATA_BLOCK);
actualMaxDataCount = maxDataCount;
bufferNum = 2;
coreRowBase_ = bcBlockFactor_ * static_cast<uint64_t>(blockIdx);
if (coreRowBase_ >= bcOuter_) {
coreRows_ = 0;
} else {
uint64_t remain = bcOuter_ - coreRowBase_;
coreRows_ = (remain < bcBlockFactor_) ? remain : bcBlockFactor_;
}
}
#endif
InitBuffers();
}
template <typename T, typename ST, typename OT>
__aicore__ inline void Mod<T, ST, OT>::ParseTilingData(const ModTilingData* tilingData)
{
blockIdx = GetBlockIdx();
coreNum = tilingData->needCoreNum;
usableUbSize = tilingData->usableUbSize;
perCoreDataCount = tilingData->perCoreDataCount;
tailCoreNum = tilingData->tailDataCoreNum;
isInput2Scalar = tilingData->isInput2Scalar;
isInput2SameShape = tilingData->isInput2SameShape;
dimNum = tilingData->dimNum;
naiveThresh_ = (tilingData->naiveThresh > 0.0f) ? tilingData->naiveThresh : 256.0f;
bcastFusedMode_ = tilingData->bcastFusedMode;
bcOuter_ = tilingData->bcOuter;
bcInner_ = tilingData->bcInner;
bcUbFormer_ = tilingData->bcUbFormer;
bcBlockFactor_ = tilingData->bcBlockFactor;
bcIpad_ = tilingData->bcIpad;
for (uint32_t i = 0; i < 8; ++i) {
input1Shape[i] = tilingData->input1Shape[i];
input2Shape[i] = tilingData->input2Shape[i];
input2Stride[i] = tilingData->input2Stride[i];
}
if (tailCoreNum == 0) {
blockOffset = perCoreDataCount * blockIdx;
} else {
if ((blockIdx + 1) <= tailCoreNum) {
perCoreDataCount += DATA_BLOCK;
blockOffset = perCoreDataCount * blockIdx;
} else {
blockOffset = ((perCoreDataCount + DATA_BLOCK) * tailCoreNum) +
(perCoreDataCount * (blockIdx - tailCoreNum));
}
}
if (blockIdx == coreNum - 1) {
perCoreDataCount = tilingData->lastCoreDataCount;
}
}
template <typename T, typename ST, typename OT>
__aicore__ inline void Mod<T, ST, OT>::InitConstants()
{
#if MOD_ENH_ARCH22
if (bcastFusedMode_ != 0) {
return;
}
if constexpr (USE_LEAN_CONTIG) {
if (isInput2Scalar || isInput2SameShape) {
return;
}
}
#endif
if constexpr (std::is_same_v<T, int>) {
#if defined(HIGH_PRECISION) && HIGH_PRECISION == 1
FP32MaxValidTensor = FP32MaxValidBuff.Get<float>();
Duplicate(FP32MaxValidTensor, FP32MaxValid, maxDataCount);
INT32MaxValidTensor = INT32MaxValidBuff.Get<int32_t>();
Duplicate(INT32MaxValidTensor, INT32MaxValid, maxDataCount);
ZeroTensor = ZeroTensorBuff.Get<float>();
Duplicate(ZeroTensor, Zero, maxDataCount);
EpsilonTensor = EpsilonTensorBuff.Get<float>();
Duplicate(EpsilonTensor, Epsilon, maxDataCount);
#endif
} else {
ZeroTensor = ZeroTensorBuff.Get<float>();
InfTensor = InfTensorBuff.Get<float>();
NanTensor = NanTensorBuff.Get<float>();
MaskTensor = MaskTensorBuff.Get<uint8_t>();
Duplicate(ZeroTensor, Zero, maxDataCount);
Duplicate(InfTensor, infValue, maxDataCount);
Duplicate(NanTensor, nanValue, maxDataCount);
}
}
template <typename T, typename ST, typename OT>
__aicore__ inline void Mod<T, ST, OT>::ProcessBroadcast(uint64_t inOffset, uint64_t outOffset)
{
uint64_t remainingDataCount = perCoreDataCount;
while (remainingDataCount > 0) {
uint32_t currentCount = maxDataCount;
if (currentCount > remainingDataCount) {
currentCount = static_cast<uint32_t>(remainingDataCount);
}
bool isConstantX2 = false;
currentCount = GetInput2ContiguousCopyCount(inOffset, currentCount, isConstantX2);
CopyIn(inOffset, currentCount, isConstantX2);
Compute(currentCount);
CopyOut(outOffset, currentCount);
inOffset += currentCount;
outOffset += currentCount;
remainingDataCount -= currentCount;
}
}
template <typename T, typename ST, typename OT>
__aicore__ inline void Mod<T, ST, OT>::ProcessContiguous(uint64_t inOffset, uint64_t outOffset)
{
uint32_t loopCount = perCoreDataCount / maxDataCount;
uint32_t tailDataCount = perCoreDataCount % maxDataCount;
for (uint32_t i = 0; i < loopCount; i++) {
CopyIn(inOffset, maxDataCount);
Compute(maxDataCount);
CopyOut(outOffset, maxDataCount);
inOffset += maxDataCount;
outOffset += maxDataCount;
}
if (tailDataCount > 0) {
CopyIn(inOffset, tailDataCount);
Compute(tailDataCount);
CopyOut(outOffset, tailDataCount);
}
}
template <typename T, typename ST, typename OT>
__aicore__ inline void Mod<T, ST, OT>::Process()
{
InitConstants();
#if MOD_ENH_ARCH22
PreScanCoreRoute();
#endif
if (!isInput2Scalar && !isInput2SameShape) {
#if MOD_ENH_ARCH22
if (bcastFusedMode_ != 0) {
ProcessFusedBcast();
} else {
ProcessBroadcast(blockOffset, blockOffset);
}
#else
ProcessBroadcast(blockOffset, blockOffset);
#endif
} else {
#if MOD_ENH_ARCH22
ProcessContigPipeline<USE_LEAN_CONTIG>(blockOffset, blockOffset);
#else
ProcessContiguous(blockOffset, blockOffset);
#endif
}
}
#include "mod_copy_impl.h"
#include "mod_int32_impl.h"
#include "mod_compute_impl.h"
#include "mod_bcast_impl.h"
#include "mod_leancontig_impl.h"
#include "mod_dispatch_impl.h"
}
#endif