* 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 foreach_cast.h
* \brief
*/
#ifndef FOREACH_CAST_N_D_H
#define FOREACH_CAST_N_D_H
#include <type_traits>
#include "kernel_operator.h"
namespace ForeachCast {
using namespace AscendC;
constexpr int32_t BUFFER_NUM = 1;
template <typename T_in, typename T_out>
class ForeachCastND {
public:
__aicore__ inline ForeachCastND(){};
__aicore__ inline void Init(GM_ADDR x, GM_ADDR y, GM_ADDR workspace, const ForeachCommonTilingData* tilingData);
__aicore__ inline void Process();
private:
template <typename T1, typename T2>
__aicore__ inline T1 CeilA2B(T1 a, T2 b)
{
if (b == 0) {
return a;
}
return (a + b - 1) / b;
};
__aicore__ inline void ParseTilingData(const ForeachCommonTilingData* tilingData);
__aicore__ inline void SingleTensorProcess(int64_t dataCount);
__aicore__ inline void CopyIn(uint16_t index, int64_t dataCount, bool isRemainder);
__aicore__ inline void ComputeAndCopyOut(uint16_t index, int64_t dataCount, bool isRemainder);
__aicore__ inline __gm__ T_in* GetInputTensorAddr(uint16_t index, GM_ADDR tensorPtr);
__aicore__ inline __gm__ T_out* GetOutputTensorAddr(uint16_t index, GM_ADDR tensorPtr);
private:
TPipe pipe;
TQue<QuePosition::VECIN, BUFFER_NUM> dataQueue;
TQue<QuePosition::VECOUT, BUFFER_NUM> outQueue;
GlobalTensor<T_in> inTensorsGM;
GlobalTensor<T_out> outTensorsGM;
GM_ADDR inTensorsPtr = nullptr;
GM_ADDR outTensorsPtr = nullptr;
int64_t blockIdx = 0;
uint32_t maxDataCount = {0};
uint64_t inputsTensorUbSize = 0;
uint64_t outputsTensorUbSize = 0;
const int64_t* tensorDataCountList = nullptr;
uint16_t tensorStart = {0};
uint16_t tensorEnd = {0};
int64_t tensorStartOffset = {0};
int64_t tensorEndOffset = {0};
};
template <typename T_in, typename T_out>
__aicore__ inline void ForeachCastND<T_in, T_out>::Init(GM_ADDR x, GM_ADDR y, GM_ADDR workspace,
const ForeachCommonTilingData* tilingData)
{
blockIdx = GetBlockIdx();
inTensorsPtr = x;
outTensorsPtr = y;
ParseTilingData(tilingData);
pipe.InitBuffer(dataQueue, BUFFER_NUM, inputsTensorUbSize);
pipe.InitBuffer(outQueue, BUFFER_NUM, outputsTensorUbSize);
maxDataCount = inputsTensorUbSize / sizeof(float);
}
template <typename T_in, typename T_out>
__aicore__ inline void ForeachCastND<T_in, T_out>::Process()
{
for (uint16_t i = tensorStart; i <= tensorEnd; i++) {
int64_t cursorStart_3 = 0;
int64_t cursorEnd = tensorDataCountList[i] - 1;
int64_t dataCount = 0;
if (i == tensorStart) {
cursorStart_3 = tensorStartOffset;
}
if (i == tensorEnd) {
cursorEnd = tensorEndOffset;
}
dataCount = cursorEnd - cursorStart_3 + 1;
inTensorsGM.SetGlobalBuffer(GetInputTensorAddr(i, inTensorsPtr) + cursorStart_3);
outTensorsGM.SetGlobalBuffer(GetOutputTensorAddr(i, outTensorsPtr) + cursorStart_3);
SingleTensorProcess(dataCount);
}
}
template <typename T_in, typename T_out>
__aicore__ inline void ForeachCastND<T_in, T_out>::SingleTensorProcess(int64_t dataCount)
{
uint32_t copyTimes = dataCount / maxDataCount;
uint32_t copyTimesRemainder = dataCount % maxDataCount;
uint32_t tempDataCount = maxDataCount;
if (copyTimesRemainder > 0) {
copyTimes++;
}
for (uint32_t i = 0; i < copyTimes; i++) {
bool isRemainder = false;
if (i == copyTimes - 1 && copyTimesRemainder > 0) {
tempDataCount = copyTimesRemainder;
isRemainder = true;
}
CopyIn(i, tempDataCount, isRemainder);
ComputeAndCopyOut(i, tempDataCount, isRemainder);
}
}
template <typename T_in, typename T_out>
__aicore__ inline void ForeachCastND<T_in, T_out>::ParseTilingData(const ForeachCommonTilingData* tilingData)
{
inputsTensorUbSize = tilingData->inputsTensorUbSize;
outputsTensorUbSize = tilingData->inputsTensorUbSize;
tensorDataCountList = tilingData->tensorDataCountList;
tensorStart = tilingData->tensorStartList[blockIdx];
tensorEnd = tilingData->tensorEndList[blockIdx];
tensorStartOffset = tilingData->tensorStartOffsetList[blockIdx];
tensorEndOffset = tilingData->tensorEndOffsetList[blockIdx];
}
template <typename T_in, typename T_out>
__aicore__ inline void ForeachCastND<T_in, T_out>::CopyIn(uint16_t index, int64_t dataCount, bool isRemainder)
{
LocalTensor<T_in> dataLocal = dataQueue.AllocTensor<T_in>();
if (isRemainder) {
DataCopyExtParams copyParams{1, static_cast<uint32_t>(dataCount * sizeof(T_in)), 0, 0, 0};
DataCopyPadExtParams<T_in> padParams{false, 0, 0, 0};
DataCopyPad(dataLocal, inTensorsGM[1ULL * index * maxDataCount], copyParams, padParams);
} else {
DataCopy(dataLocal, inTensorsGM[1ULL * index * maxDataCount], dataCount);
}
dataQueue.EnQue(dataLocal);
}
template <typename T_in, typename T_out>
__aicore__ inline void ForeachCastND<T_in, T_out>::ComputeAndCopyOut(uint16_t index, int64_t dataCount,
bool isRemainder)
{
LocalTensor<T_in> dataLocal = dataQueue.DeQue<T_in>();
LocalTensor<T_out> outLocal = outQueue.AllocTensor<T_out>();
PipeBarrier<PIPE_V>();
if constexpr (IsSameType<T_out, float>::value) {
Cast(outLocal, dataLocal, RoundMode::CAST_NONE, dataCount);
PipeBarrier<PIPE_V>();
} else {
Cast(outLocal, dataLocal, RoundMode::CAST_RINT, dataCount);
PipeBarrier<PIPE_V>();
}
event_t eventIDVToMTE3 = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::V_MTE3));
SetFlag<HardEvent::V_MTE3>(eventIDVToMTE3);
WaitFlag<HardEvent::V_MTE3>(eventIDVToMTE3);
if (isRemainder) {
DataCopyExtParams copyParams{1, static_cast<uint32_t>(dataCount * sizeof(T_out)), 0, 0, 0};
DataCopyPad(outTensorsGM[1ULL * index * maxDataCount], outLocal, copyParams);
} else {
DataCopy(outTensorsGM[1ULL * index * maxDataCount], outLocal, dataCount);
}
event_t eventIDMTE3ToMTE2 = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE3_MTE2));
SetFlag<HardEvent::MTE3_MTE2>(eventIDMTE3ToMTE2);
WaitFlag<HardEvent::MTE3_MTE2>(eventIDMTE3ToMTE2);
dataQueue.FreeTensor(dataLocal);
outQueue.FreeTensor(outLocal);
}
template <typename T_in, typename T_out>
__aicore__ inline __gm__ T_in* ForeachCastND<T_in, T_out>::GetInputTensorAddr(uint16_t index, GM_ADDR tensorPtr)
{
__gm__ uint64_t* dataAddr = reinterpret_cast<__gm__ uint64_t*>(tensorPtr);
uint64_t tensorPtrOffset = *dataAddr;
__gm__ uint64_t* retPtr = dataAddr + (tensorPtrOffset >> 3);
return reinterpret_cast<__gm__ T_in*>(*(retPtr + index));
}
template <typename T_in, typename T_out>
__aicore__ inline __gm__ T_out* ForeachCastND<T_in, T_out>::GetOutputTensorAddr(uint16_t index, GM_ADDR tensorPtr)
{
__gm__ uint64_t* dataAddr = reinterpret_cast<__gm__ uint64_t*>(tensorPtr);
uint64_t tensorPtrOffset = *dataAddr;
__gm__ uint64_t* retPtr = dataAddr + (tensorPtrOffset >> 3);
return reinterpret_cast<__gm__ T_out*>(*(retPtr + index));
}
}
#endif