/**
 * Copyright (c) 2025 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 "fusion_c.hpp"
#include "stream_c.hpp"
#include "context.hpp"
#include "error_message_manage.hpp"
#include "task_david.hpp"
#include "thread_local_container.hpp"
#include "inner_thread_local.hpp"
#include "stream.hpp"
#include "task_submit.hpp"
#include "profiler_c.hpp"
#include "aix_c.hpp"
#include "task_execute_time.h"
#include "fusion_task.h"
#include "enum_desc.hpp"

namespace cce {
namespace runtime {
TIMESTAMP_EXTERN(rtFusionLaunch_ArgLoadAll);
TIMESTAMP_EXTERN(rtLaunchKernel_SubMit);

constexpr uint8_t RT_FUSION_CCU_DIE0_BIT_MOVE = 3U;
constexpr uint8_t RT_FUSION_CCU_DIE1_BIT_MOVE = 4U;
constexpr uint8_t RT_MISSION_INDEX_ONE = 1U;
constexpr uint8_t RT_MISSION_INDEX_TWO = 2U;
constexpr uint8_t RT_MISSION_INDEX_THREE = 3U;
constexpr uint8_t RT_MISSION_INDEX_FOUR = 4U;

void AixKernelTaskInitForFusion(
    TaskInfo* const taskInfo, const rtAicAivFusionInfo_t* const aicAivInfo,
    const LaunchTaskCfgInfo_t* const launchTaskCfg)
{
    FusionTaskInfo* fusionTaskInfo = &(taskInfo->u.fusionKernelTask);
    FusionTaskInfoAicPart* aicPart = &(fusionTaskInfo->aicPart);

    GetAicAivTypeForFusion(aicAivInfo->mixType, aicAivInfo->kernelAttrType, fusionTaskInfo->aicAivType);

    aicPart->dim = aicAivInfo->dimNum;
    aicPart->kernel = aicAivInfo->kernel;

    aicPart->kernelFlag = static_cast<uint8_t>(launchTaskCfg->dumpflag & 0xFFU);
    aicPart->qos = launchTaskCfg->qos;
    aicPart->partId = launchTaskCfg->partId;
    aicPart->schemMode = launchTaskCfg->schemMode;
    if (aicPart->schemMode == static_cast<uint8_t>(RT_SCHEM_MODE_END)) {
        aicPart->schemMode =
            (aicPart->kernel == nullptr) ? static_cast<uint8_t>(RT_SCHEM_MODE_NORMAL) : aicPart->kernel->GetSchedMode();
    }

    aicPart->dynamicShareMemSize = 0U;
    aicPart->simtDcuSmSize = RT_SIMT_UB_SIZE;
    aicPart->groupDim = 0U;
    aicPart->groupBlockDim = 0U;
    aicPart->funcAddr = aicAivInfo->funcAddr;
    aicPart->funcAddr1 = aicAivInfo->funcAddr1;
    rtArgsSizeInfo_t& argsSize = ThreadLocalContainer::GetArgsSizeInfo();
    if (argsSize.infoAddr != nullptr) {
        aicPart->inputArgsSize.infoAddr = argsSize.infoAddr;
        aicPart->inputArgsSize.atomicIndex = argsSize.atomicIndex;
        argsSize.infoAddr = nullptr;
        argsSize.atomicIndex = 0U;
        RT_LOG(
            RT_LOG_INFO, "infoAddr=%p, atomicIndex=%u.", aicPart->inputArgsSize.infoAddr,
            aicPart->inputArgsSize.atomicIndex);
    } else {
        aicPart->inputArgsSize.infoAddr = nullptr;
        aicPart->inputArgsSize.atomicIndex = 0U;
    }
    const uint32_t taskRation = (aicPart->kernel != nullptr) ? aicPart->kernel->GetTaskRation() : 0U;
    RT_LOG(
        RT_LOG_INFO,
        "aicAivType=%hhu, taskRation=%u, kernelFlag=0x%x, qos=%u,"
        " partId=%u, schemMode=%u.",
        fusionTaskInfo->aicAivType, taskRation, aicPart->kernelFlag, aicPart->qos, aicPart->partId, aicPart->schemMode);
}

void FusionKernelTaskInit(TaskInfo* taskInfo)
{
    TaskCommonInfoInit(taskInfo);
    FusionTaskInfo* fusionTaskInfo = &(taskInfo->u.fusionKernelTask);

    taskInfo->type = TS_TASK_TYPE_FUSION_KERNEL;
    taskInfo->typeName = const_cast<char_t*>("FUSION_KERNEL");
    fusionTaskInfo->argHandle = nullptr;
    fusionTaskInfo->argsInfo = nullptr;
    return;
}

static rtError_t CheckFusionDynSizeValid(TaskInfo* taskInfo, const rtFunsionTaskInfo_t* const fusionKernel)
{
    if ((taskInfo == nullptr) || (fusionKernel == nullptr)) {
        return RT_ERROR_NONE;
    }

    const uint32_t subKernelNum = fusionKernel->subTaskNum;
    for (uint32_t idx = 0; idx < subKernelNum; idx++) {
        const uint32_t subKernelType = fusionKernel->subTask[idx].type;
        if (subKernelType == RT_FUSION_AICORE) {
            FusionTaskInfoAicPart* aicPart = &(taskInfo->u.fusionKernelTask.aicPart);
            Kernel* kernel = aicPart->kernel;
            if (kernel == nullptr) {
                continue;
            }

            const rtError_t ret = CheckAndGetTotalShareMemorySize(
                kernel, taskInfo->stream, aicPart->dynamicShareMemSize, aicPart->simtDcuSmSize);
            if (ret != RT_ERROR_NONE) {
                return ret;
            }
        }
    }
    return RT_ERROR_NONE;
}

static rtError_t PreProcAicAivTaskForFusion(
    rtFusionSubTaskInfo_t* const fusionTask, const Stream* const stm, Program*& prog, rtAicAivFusionInfo_t* aicAivInfo,
    LaunchTaskCfgInfo_t* launchTaskCfg)
{
    rtError_t error = RT_ERROR_NONE;
    rtAicoreFusionInfo_t* const aicInfo = &(fusionTask->task.aicoreInfo);
    void* const stubFunc = aicInfo->stubFunc;
    void* const progHandle = aicInfo->hdl;
    Program* realProgram = nullptr;
    COND_RETURN_AND_MSG_OUTER(
        (stubFunc == nullptr && progHandle == nullptr) || (stubFunc != nullptr && progHandle != nullptr),
        RT_ERROR_INVALID_VALUE, ErrorCode::EE1017, "AIC/AIV subtask preprocessing", "stubFunc/progHandle",
        "exactly one of stubFunc or progHandle must be non-null");

    rtLaunchConfig_t* cfgInfo = aicInfo->config;
    NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(cfgInfo, RT_ERROR_INVALID_VALUE, "AIC/AIV subtask preprocessing");
    NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(cfgInfo->attrs, RT_ERROR_INVALID_VALUE, "AIC/AIV subtask preprocessing");
    error = GetLaunchConfigInfo(cfgInfo, launchTaskCfg);
    COND_RETURN_ERROR_MSG_INNER(
        error != RT_ERROR_NONE, error, "Kernel launch GetLaunchConfigInfo failed, stream_id=%d.", stm->Id_());

    uint64_t tilingKey = 0ULL;
    if (progHandle != nullptr) {
        const rtError_t handleRet = GetValidatedObject<Program>(progHandle, realProgram);
        ERROR_RETURN(
            handleRet, "Failed to validate fusion program handle, retCode=%#x.", static_cast<uint32_t>(handleRet));
        tilingKey = aicInfo->tilingKey;
    }

    const uint32_t coreDim = launchTaskCfg->blockDim;
    rtKernelAttrType kernelAttrType = RT_KERNEL_ATTR_TYPE_AICORE;
    uint8_t mixType = NO_MIX;
    uint64_t addr1 = 0ULL;
    uint64_t addr2 = 0ULL;
    Module* launchMdl = nullptr;
    Kernel* registeredKernel = nullptr;

    error = StreamLaunchKernelPrepare(
        stm, registeredKernel, prog, kernelAttrType, launchMdl, stubFunc, addr1, addr2, realProgram, tilingKey);
    ERROR_RETURN(
        error, "Failed to prepare kernel launch, stream_id=%d, retCode=%#x.", stm->Id_(), static_cast<uint32_t>(error));

    mixType = registeredKernel->GetMixType();
    aicAivInfo->kernelAttrType = kernelAttrType;
    aicAivInfo->dimNum = coreDim;
    aicAivInfo->mixType = mixType;
    aicAivInfo->funcAddr = addr1;
    aicAivInfo->funcAddr1 = addr2;
    aicAivInfo->kernel = registeredKernel;
    aicAivInfo->program = (prog != nullptr) ? prog : realProgram;
    RT_LOG(
        RT_LOG_INFO,
        "kernel info: stream_id=%d, kernelAttrType=%s, kernel_name=%s,"
        "mixType=%u, taskRation=%u, tilingKey=%llu.",
        stm->Id_(), KernelAttrTypeToString(kernelAttrType).c_str(), registeredKernel->Name_().c_str(), mixType,
        registeredKernel->GetTaskRation(), tilingKey);
    return RT_ERROR_NONE;
}

static bool CheckCcuMissionIdSingleDie(rtCcuTaskInfo_t* ccuTaskInfo, uint32_t num)
{
    if (num == 1) {
        return true;
    }
    return (ccuTaskInfo[num - 1].missionId > ccuTaskInfo[num - 2].missionId) &&
           CheckCcuMissionIdSingleDie(ccuTaskInfo, num - 1);
}

static bool CheckCcuMissionIdDoubleDie(rtCcuTaskInfo_t* ccuTaskInfo, uint32_t num)
{
    if (num == RT_MISSION_INDEX_TWO) {
        return true;
    }
    return (ccuTaskInfo[num - RT_MISSION_INDEX_ONE].missionId > ccuTaskInfo[num - RT_MISSION_INDEX_THREE].missionId) &&
           (ccuTaskInfo[num - RT_MISSION_INDEX_TWO].missionId > ccuTaskInfo[num - RT_MISSION_INDEX_FOUR].missionId) &&
           CheckCcuMissionIdDoubleDie(ccuTaskInfo, num - RT_MISSION_INDEX_TWO);
}

static bool CheckCcuMissionId(rtCcuTaskInfo_t* ccuTaskInfo, uint32_t num, bool isDoubleDie)
{
    if (isDoubleDie) {
        return CheckCcuMissionIdDoubleDie(ccuTaskInfo, num);
    }
    return CheckCcuMissionIdSingleDie(ccuTaskInfo, num);
}

static rtError_t PreProcCcuTaskForFusion(
    rtFusionSubTaskInfo_t* const fusionTask, uint8_t& sqeSubType, uint32_t& sqeLen, uint8_t& ccuArgSize,
    Stream* const stm)
{
    rtCcuTaskGroup_t* const ccuInfo = &(fusionTask->task.ccuInfo);
    COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
        ccuInfo->taskNum == 0U || ccuInfo->taskNum > FUSION_SUB_TASK_MAX_CCU_NUM, RT_ERROR_INVALID_VALUE,
        "CCU subtask preprocessing", ccuInfo->taskNum, "(0, " + std::to_string(FUSION_SUB_TASK_MAX_CCU_NUM) + "]");
    NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(ccuInfo->ccuTaskInfo, RT_ERROR_INVALID_VALUE, "CCU subtask preprocessing");

    uint8_t isHas128B = 0U;
    uint8_t isHas32B = 0U;
    uint32_t dieIdArrange = 0U;
    uint32_t checkArrange = 0U;
    const uint32_t devDieNum = stm->Device_()->GetDevProperties().ioDieNum;
    const uint32_t dieNum = (devDieNum != 0U) ? devDieNum : 2U;

    // get ccu subType
    for (uint32_t i = 0U; i < ccuInfo->taskNum; i++) {
        NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
            ccuInfo->ccuTaskInfo[i].args, RT_ERROR_INVALID_VALUE, "CCU subtask preprocessing");
        COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
            ccuInfo->ccuTaskInfo[i].argSize != RT_CCU_SQE128B_ARGS_SIZE &&
                ccuInfo->ccuTaskInfo[i].argSize != RT_CCU_SQE32B_ARGS_SIZE,
            RT_ERROR_INVALID_VALUE, "CCU subtask preprocessing", ccuInfo->ccuTaskInfo[i].argSize,
            std::to_string(RT_CCU_SQE32B_ARGS_SIZE) + " or " + std::to_string(RT_CCU_SQE128B_ARGS_SIZE));
        COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
            ccuInfo->ccuTaskInfo[i].dieId >= dieNum, RT_ERROR_INVALID_VALUE, "CCU subtask preprocessing",
            static_cast<uint32_t>(ccuInfo->ccuTaskInfo[i].dieId), "[0, " + std::to_string(dieNum) + ")");
        COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
            ccuInfo->ccuTaskInfo[i].missionId > RT_CCU_MISSION_ID_MAX, RT_ERROR_INVALID_VALUE,
            "CCU subtask preprocessing", ccuInfo->ccuTaskInfo[i].missionId,
            "[0, " + std::to_string(RT_CCU_MISSION_ID_MAX) + "]");
        COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
            ccuInfo->ccuTaskInfo[i].instCnt == RT_CCU_INST_CNT_INVALID, RT_ERROR_INVALID_VALUE,
            "CCU subtask preprocessing", ccuInfo->ccuTaskInfo[i].instCnt, "not equal to 0");
        COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
            ccuInfo->ccuTaskInfo[i].instStartId >= RT_CCU_INST_START_MAX, RT_ERROR_INVALID_VALUE,
            "CCU subtask preprocessing", ccuInfo->ccuTaskInfo[i].instStartId,
            "[0, " + std::to_string(RT_CCU_INST_START_MAX) + ")");
        sqeSubType |= (ccuInfo->ccuTaskInfo[i].dieId == 0U) ? (1U << RT_FUSION_CCU_DIE0_BIT_MOVE) :
                                                              (1U << RT_FUSION_CCU_DIE1_BIT_MOVE);
        // 双die上 0、1排布合法性
        dieIdArrange |= static_cast<uint32_t>(ccuInfo->ccuTaskInfo[i].dieId) << i;
        checkArrange = ((i & 1U) == 1U) ? checkArrange : ((checkArrange << 2U) | 0x2U);
        isHas32B |= ccuInfo->ccuTaskInfo[i].argSize == RT_CCU_SQE32B_ARGS_SIZE ? 1U : 0U;
        isHas128B |= ccuInfo->ccuTaskInfo[i].argSize == RT_CCU_SQE128B_ARGS_SIZE ? 1U : 0U;
    }
    COND_RETURN_AND_MSG_OUTER(
        (isHas32B == 1U) && (isHas128B == 1U), RT_ERROR_INVALID_VALUE, ErrorCode::EE1017, "CCU subtask preprocessing",
        "argSize", "all CCU subtasks must use the same argSize, either 32B or 128B");

    // 双die num奇数返错 0x18 -> 11000
    const bool isDoubleDie = (sqeSubType & 0x18U) == 0x18U ? true : false;
    uint32_t taskNum = ccuInfo->taskNum;
    if (isDoubleDie) {
        COND_RETURN_AND_MSG_OUTER(
            ((taskNum & 1U) == 1U), RT_ERROR_INVALID_VALUE, ErrorCode::EE1011, "CCU subtask preprocessing",
            static_cast<uint32_t>(taskNum), "taskNum", "taskNum must be an even number when double die is used");
        COND_RETURN_AND_MSG_OUTER(
            (dieIdArrange != checkArrange), RT_ERROR_INVALID_VALUE, ErrorCode::EE1017, "CCU subtask preprocessing",
            "dieIdArrange", "die IDs must alternate between die 0 and die 1 when double die is used");
        taskNum = taskNum / 2U;
    }

    COND_RETURN_AND_MSG_OUTER(
        (isHas32B == 1U) && (taskNum <= 1U), RT_ERROR_INVALID_VALUE, ErrorCode::EE1011, "CCU subtask preprocessing",
        static_cast<uint32_t>(taskNum), "taskNum", "taskNum must be greater than 1 when argSize is 32B per die");
    COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
        (isHas128B == 1U) && (taskNum > 1U), RT_ERROR_INVALID_VALUE, "CCU subtask preprocessing", taskNum, "1");
    ccuArgSize = (isHas32B == 1U) ? RT_CCU_SQE32B_ARGS_SIZE : RT_CCU_SQE128B_ARGS_SIZE;
    // mission id的递增
    COND_RETURN_AND_MSG_OUTER(
        !CheckCcuMissionId(ccuInfo->ccuTaskInfo, ccuInfo->taskNum, isDoubleDie), RT_ERROR_INVALID_VALUE,
        ErrorCode::EE1017, "CCU subtask preprocessing", "missionId",
        "CCU subtask mission IDs must be strictly increasing");

    // when fusion type is "ccu + aic" or "ccu + ccu", ccu sqe' size must be occupied 4*64B=256B.
    sqeLen += 4U;

    RT_LOG(RT_LOG_INFO, "ccu task info: sqeSubType=%hhu, taskNum=%u.", sqeSubType, ccuInfo->taskNum);
    return RT_ERROR_NONE;
}

static void FusionKernelTaskProc(
    const rtFunsionTaskInfo_t* const fusionKernel, TaskInfo* const taskInfo,
    const rtAicAivFusionInfo_t* const aicAivInfo, const LaunchTaskCfgInfo_t* const launchTaskCfg, uint8_t ccuArgSize)
{
    const uint32_t subKernelNum = fusionKernel->subTaskNum;
    FusionKernelTaskInit(taskInfo);
    for (uint32_t idx = 0; idx < subKernelNum; idx++) {
        const rtFusionType_t subKernelType = fusionKernel->subTask[idx].type;
        const rtFusionSubTaskInfo_t* const subKernelInfo = &(fusionKernel->subTask[idx]);
        switch (subKernelType) {
            case RT_FUSION_AICORE:
                AixKernelTaskInitForFusion(taskInfo, aicAivInfo, launchTaskCfg);
                break;
            case RT_FUSION_CCU:
                taskInfo->u.fusionKernelTask.ccuSqeNum = subKernelInfo->task.ccuInfo.taskNum;
                taskInfo->u.fusionKernelTask.ccuArgSize = ccuArgSize;
                break;
            default:
                RT_LOG(RT_LOG_INFO, "sub task [%u]'s type=%u.", idx, static_cast<uint32_t>(subKernelType));
                break;
        }
    }
}

static rtError_t FusionKernelTaskPreProc(
    rtFunsionTaskInfo_t* const fusionKernel, Stream* const stm, Program*& prog, uint32_t& sqeLen, uint8_t& sqeSubType,
    uint8_t& ccuArgSize, rtAicAivFusionInfo_t* aicAivInfo, LaunchTaskCfgInfo_t* launchTaskCfg)
{
    rtError_t error = RT_ERROR_NONE;
    const uint32_t subKernelNum = fusionKernel->subTaskNum;

    std::array<uint32_t, RT_FUSION_CCU> fusionSubTaskMove = {
        RT_FUSION_HCOMCPU_BIT_MOVE, RT_FUSION_AICPU_BIT_MOVE, RT_FUSION_AIC_BIT_MOVE};
    for (uint32_t idx = 0; idx < subKernelNum; idx++) {
        uint32_t subKernelType = fusionKernel->subTask[idx].type;
        rtFusionSubTaskInfo_t* const subKernelInfo = &(fusionKernel->subTask[idx]);
        switch (subKernelType) {
            case RT_FUSION_AICORE:
                error = PreProcAicAivTaskForFusion(subKernelInfo, stm, prog, aicAivInfo, launchTaskCfg);
                ERROR_RETURN(
                    error, "stream_id=%d failed to proc aic/aiv sub task, retCode=%#x.", stm->Id_(),
                    static_cast<uint32_t>(error));
                sqeSubType |= (1U << fusionSubTaskMove[subKernelType]);
                sqeLen += 1U;
                break;
            case RT_FUSION_HCOM_CPU:
                COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
                    fusionKernel->subTask[idx].task.aicpuInfo.blockDim != 1U, RT_ERROR_INVALID_VALUE,
                    "Fusion kernel task preprocessing", fusionKernel->subTask[idx].task.aicpuInfo.blockDim, "1");
                subKernelType += (stm->Device_()->IsSupportHcomcpu() == 1U) ? 0U : 1U;
                sqeSubType |= (1U << fusionSubTaskMove[subKernelType]);
                sqeLen += 1U;
                break;
            case RT_FUSION_AICPU:
                sqeSubType |= (1U << FUSION_SUBTASK_MOVE[subKernelType]);
                sqeLen += 1U;
                break;
            case RT_FUSION_CCU:
                error = PreProcCcuTaskForFusion(subKernelInfo, sqeSubType, sqeLen, ccuArgSize, stm);
                ERROR_RETURN(
                    error, "stream_id=%d failed to proc ccu sub task, retCode=%#x", stm->Id_(),
                    static_cast<uint32_t>(error));
                /* 0x18U means die0 ccu + die1 ccu */
                COND_RETURN_AND_MSG_OUTER(
                    (subKernelNum == 1U) && ((sqeSubType != 0x18U) || (ccuArgSize != RT_CCU_SQE128B_ARGS_SIZE)),
                    RT_ERROR_INVALID_VALUE, ErrorCode::EE1017, "Fusion kernel task preprocessing", "ccuArgSize",
                    "CCU-only fusion task requires double die with 128B argSize");
                break;
            default:
                RT_LOG_OUTER_MSG_WITH_FUNC_DESC(
                    ErrorCode::EE1003, "Fusion kernel task preprocessing",
                    RtFmtMsg("%s(%u)", (subKernelType == RT_FUSION_END) ? "FUSION_END" : "UNKNOWN", subKernelType),
                    "subKernelType", "[0, 3]");
                return RT_ERROR_INVALID_VALUE;
        }
    }
    RT_LOG(RT_LOG_INFO, "sqeLen=%hhu, sqeSubType=%hhu, stream_id=%d.", sqeLen, sqeSubType, stm->Id_());
    return error;
}

static void SetArgsFusionKernel(
    const rtFusionArgsEx_t* const argsInfo, TaskInfo* const taskInfo, StarsArgLoaderResult* const result)
{
    FusionTaskInfo* const fusionKernelTask = &(taskInfo->u.fusionKernelTask);
    fusionKernelTask->argsInfo = const_cast<rtFusionArgsEx_t*>(argsInfo);
    fusionKernelTask->argsSize = argsInfo->argsSize;
    fusionKernelTask->args = result->kerArgs;

    const uint8_t aicpuNum = argsInfo->aicpuNum;
    for (uint8_t i = 0U; i < aicpuNum; ++i) {
        fusionKernelTask->aicpuArgsDesc[i].soName =
            static_cast<void*>(RtPtrToPtr<char_t*>(fusionKernelTask->args) + argsInfo->aicpuArgs[i].soNameAddrOffset);
        fusionKernelTask->aicpuArgsDesc[i].funcName = static_cast<void*>(
            RtPtrToPtr<char_t*>(fusionKernelTask->args) + argsInfo->aicpuArgs[i].kernelNameAddrOffset);
    }

    // only for aclgraph
    fusionKernelTask->oldArgHandle = fusionKernelTask->argHandle;

    if (result->handle != nullptr) {
        fusionKernelTask->argHandle = result->handle;
        taskInfo->needPostProc = true;
    }
    taskInfo->stmArgPos = static_cast<DavidStream*>(taskInfo->stream)->GetArgPos();
    result->stmArgPos = UINT32_MAX;
    result->handle = nullptr;
}

static rtError_t CheckUpdateFusionTaskInfo(
    const TaskInfo* const updateTask, uint32_t sqeLen, uint8_t sqeSubType, const Kernel* const kernel,
    uint8_t kernelType, const Stream* const stm)
{
    COND_RETURN_AND_MSG_OUTER(
        updateTask->stream->Model_() == nullptr, RT_ERROR_MODEL_NULL, ErrorCode::EE1017,
        "Checking and updating fusion task information", "task", "The update task must be a sinked task");

    COND_RETURN_AND_MSG_OUTER(
        stm->Model_() != nullptr, RT_ERROR_STREAM_MODEL, ErrorCode::EE1017,
        "Checking and updating fusion task information", "stream",
        RtFmtMsg(
            "The update stream (stream_id=%d) must be a single operator stream, not bound to a model (model_id=%u)",
            stm->Id_(), stm->Model_()->Id_()));

    COND_RETURN_AND_MSG_OUTER(
        updateTask->u.fusionKernelTask.sqeLen != sqeLen, RT_ERROR_INVALID_VALUE, ErrorCode::EE1011,
        "Checking and updating fusion task information", static_cast<uint32_t>(sqeLen), "sqeLen",
        "sqeLen must match the original fusion task's sqeLen (" +
            std::to_string(updateTask->u.fusionKernelTask.sqeLen) + ")");

    COND_RETURN_AND_MSG_OUTER(
        (sqeSubType & (1U << RT_FUSION_AIC_BIT_MOVE)) == 0U, RT_ERROR_INVALID_VALUE, ErrorCode::EE1017,
        "Checking and updating fusion task information", "sqeSubType",
        "The update subtask must include the AI Core task");

    COND_RETURN_AND_MSG_OUTER(
        (updateTask->u.fusionKernelTask.aicPart.kernel->GetMixType() != kernel->GetMixType()) ||
            (updateTask->u.fusionKernelTask.aicPart.kernel->GetFuncType() != kernel->GetFuncType()) ||
            (updateTask->u.fusionKernelTask.aicAivType != kernelType),
        RT_ERROR_KERNEL_TYPE, ErrorCode::EE1017, "Checking and updating fusion task information", "kernelType",
        "kernel type (mixType, funcType, machine) must match the original fusion task");

    return RT_ERROR_NONE;
}

void GetAicAivTypeForFusion(uint8_t mixType, rtKernelAttrType kernelAttrType, uint8_t& aicAivType)
{
    if (mixType != static_cast<uint8_t>(NO_MIX)) {
        if ((mixType == static_cast<uint8_t>(MIX_AIC)) || (mixType == static_cast<uint8_t>(MIX_AIC_AIV_MAIN_AIC))) {
            aicAivType = 0U;
        } else {
            aicAivType = 1U;
        }
    } else {
        if (kernelAttrType == RT_KERNEL_ATTR_TYPE_VECTOR) {
            aicAivType = 1U;
        } else {
            aicAivType = 0U;
        }
    }
}

rtError_t LaunchFusionKernel(Stream* stm, void* const fusionKernelInfo, rtFusionArgsEx_t* argsInfo)
{
    StarsArgLoaderResult result = {nullptr, nullptr, nullptr, UINT32_MAX, nullptr, nullptr};
    Program* prog = nullptr;
    Runtime* const rt = Runtime::Instance();
    rtFunsionTaskInfo_t* fusionKernel = static_cast<rtFunsionTaskInfo_t*>(fusionKernelInfo);

    uint32_t sqeLen = 0U;
    uint8_t sqeSubType = 0U;
    uint8_t ccuArgSize = 0U;
    rtAicAivFusionInfo_t aicAivInfo = {};
    LaunchTaskCfgInfo_t launchTaskCfg = {};

    rtError_t error =
        FusionKernelTaskPreProc(fusionKernel, stm, prog, sqeLen, sqeSubType, ccuArgSize, &aicAivInfo, &launchTaskCfg);
    ERROR_PROC_RETURN_MSG_INNER(error, rt->PutProgram(prog);
                                , "Failed to pre proc fusion kernel task, retCode=%#x.", static_cast<uint32_t>(error));
    if (sqeLen > SQE_NUM_PER_DAVID_TASK_MAX) {
        rt->PutProgram(prog);
        RT_LOG_OUTER_MSG_INVALID_PARAM_WITH_DESC(
            "Delivering a kernel fusion task", sqeLen, "(0, " + std::to_string(SQE_NUM_PER_DAVID_TASK_MAX) + "]");
        return RT_ERROR_INVALID_VALUE;
    }
    error = CheckTaskCanSend(stm);
    ERROR_PROC_RETURN_MSG_INNER(error, rt->PutProgram(prog);
                                , "stream_id=%d check failed, retCode=%#x.", stm->Id_(), static_cast<uint32_t>(error));
    DavidStream* davidStm = static_cast<DavidStream*>(stm);
    const bool useArgPool = UseArgsPool(davidStm, argsInfo, stm->IsTaskGroupUpdate());
    uint32_t pos = 0xFFFFU;
    Stream* dstStm = stm;
    TaskInfo* taskInfo = nullptr;
    std::function<void()> const errRecycle = [&result, &taskInfo, &stm, &pos, &prog, &dstStm]() {
        StreamLaunchKernelRecycle(result, taskInfo, prog, stm);
        TaskRollBack(dstStm, pos);
        stm->StreamUnLock();
    };
    stm->StreamLock();
    taskInfo = stm->AllocTask(nullptr, TS_TASK_TYPE_FUSION_KERNEL, error, sqeLen, UpdateTaskFlag::SUPPORT);
    COND_PROC_RETURN_ERROR_MSG_INNER(taskInfo == nullptr, error, stm->StreamUnLock(); rt->PutProgram(prog);
                                     , "stream_id=%d alloc task failed, retCode=%#x.", stm->Id_(),
                                     static_cast<uint32_t>(error));
    ScopeGuard tskErrRecycle(errRecycle);
    if (taskInfo->isUpdateSinkSqe == 1U) {
        uint8_t aicAivType = 0U;
        GetAicAivTypeForFusion(aicAivInfo.mixType, aicAivInfo.kernelAttrType, aicAivType);
        error = CheckUpdateFusionTaskInfo(taskInfo, sqeLen, sqeSubType, aicAivInfo.kernel, aicAivType, stm);
        ERROR_RETURN(
            error, "Failed to check update fusion task info, stream_id=%d, kernelType=%u, retCode=%#x.", stm->Id_(),
            aicAivType, static_cast<uint32_t>(error));
    } else {
        pos = taskInfo->id;
        dstStm = taskInfo->stream;
    }
    FusionKernelTaskProc(fusionKernel, taskInfo, &aicAivInfo, &launchTaskCfg, ccuArgSize);

    // for simt
    error = CheckFusionDynSizeValid(taskInfo, fusionKernel);
    ERROR_RETURN_MSG_INNER(
        error, "Failed to check SimtSmSize, stream_id=%d, retCode=%#x.", stm->Id_(), static_cast<uint32_t>(error));
    if (argsInfo != nullptr) {
        error = static_cast<DavidStream*>(dstStm)->LoadArgsInfo(argsInfo, useArgPool, &result);
        ERROR_RETURN_MSG_INNER(
            error, "Failed to load args with stm pool, stream_id=%d, useArgPool=%u, retCode=%#x.", stm->Id_(),
            useArgPool, static_cast<uint32_t>(error));
        SetArgsFusionKernel(argsInfo, taskInfo, &result);
    }
    taskInfo->stmArgPos = static_cast<DavidStream*>(dstStm)->GetArgPos();
    taskInfo->u.fusionKernelTask.fusionKernelInfo = const_cast<void*>(fusionKernelInfo);
    taskInfo->u.fusionKernelTask.sqeLen = sqeLen;
    taskInfo->u.fusionKernelTask.sqeSubType = sqeSubType;
    RT_LOG(RT_LOG_INFO, "sqeLen=%hhu, sqeSubType=%hhu, stream_id=%d.", sqeLen, sqeSubType, stm->Id_());
    if (taskInfo->isUpdateSinkSqe == 1U) {
        error = UpdateDavidKernelTaskSubmit(taskInfo, stm, sqeLen);
        if ((error != RT_ERROR_NONE) && (taskInfo->u.fusionKernelTask.oldArgHandle != nullptr)) {
            if (davidStm->ArgManagePtr() != nullptr) {
                davidStm->ArgManagePtr()->RecycleDevLoader(taskInfo->u.fusionKernelTask.oldArgHandle);
            }
        }
        taskInfo->u.fusionKernelTask.oldArgHandle = nullptr;
        taskInfo->isUpdateSinkSqe = 0U;
    } else {
        error = DavidSendTask(taskInfo, dstStm);
    }
    ERROR_RETURN_MSG_INNER(
        error, "Submit task failed, stream_id=%d, retCode=%#x.", stm->Id_(), static_cast<uint32_t>(error));
    tskErrRecycle.ReleaseGuard();
    stm->StreamUnLock();
    rt->PutProgram(prog);
    SET_THREAD_TASKID_AND_STREAMID(dstStm->GetExposedStreamId(), taskInfo->taskSn);
    error = SubmitTaskPostProc(dstStm, pos);
    ERROR_RETURN_MSG_INNER(
        error, "Post-processing of task submission failed, stream_id=%d, retCode=%#x.", stm->Id_(),
        static_cast<uint32_t>(error));
    return RT_ERROR_NONE;
}

} // namespace runtime
} // namespace cce