* 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
* the 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 causal_conv1d_tiling.cpp
* \brief Host-side tiling — shape parsing, UB budget, block decomposition.
*/
#include <algorithm>
#include <limits>
#include <cstring>
#include "log/log.h"
#include "op_common/op_host/util/platform_util.h"
#include "op_host/tiling_util.h"
#include "op_host/tiling_templates_registry.h"
#include "util/math_util.h"
#include "../op_kernel/arch35/causal_conv1d_tiling_data.h"
#include "../op_kernel/arch35/causal_conv1d_tiling_key.h"
namespace optiling {
namespace CausalConv1d {
using namespace Ops::Transformer::OpTiling;
constexpr uint32_t X_INDEX = 0;
constexpr uint32_t WEIGHT_INDEX = 1;
constexpr uint32_t CONV_STATES_INDEX = 2;
constexpr uint32_t BIAS_INDEX = 3;
constexpr uint32_t QUERY_START_LOC_INDEX = 4;
constexpr uint32_t CACHE_INDICES_INDEX = 5;
constexpr uint32_t INITIAL_STATE_MODE_INDEX = 6;
constexpr uint32_t NUM_ACCEPTED_TOKENS_INDEX = 7;
constexpr int64_t ASCENDC_RESERVED_WORKSPACE_SIZE = 16 * 1024 * 1024;
constexpr int64_t DIM_ALIGN_BYTES = 32;
static int64_t FloorPow2(int64_t n)
{
if (n <= 0) {
return 0;
}
uint64_t result = 1;
const uint64_t un = static_cast<uint64_t>(n);
while (result <= un && result <= (UINT64_MAX >> 1)) {
result <<= 1;
}
return static_cast<int64_t>(result >> 1);
}
static int64_t FloorPow2Multiple(int64_t n, int64_t base)
{
if (n < base || base <= 0) {
return base > 0 ? base : 0;
}
return FloorPow2(n / base) * base;
}
struct CausalConv1dCompileInfo {};
struct CausalConv1dAttrInfo {
int64_t activationMode = 0;
int64_t nullBlockId = 0;
};
struct CausalConv1dInputInfo {
int64_t dim = 0;
int64_t batch = 0;
int64_t seqLen = 0;
int64_t cuSeqlen = 0;
int64_t kernelWidth = 0;
int64_t stateLen = 0;
int64_t numCacheLines = 0;
int64_t elemBytes = 0;
uint32_t inputModeKey = CAUSAL_CONV1D_TPL_INPUT_MODE_FN_VARLEN;
uint32_t widthKey = CAUSAL_CONV1D_TPL_WIDTH_2;
uint32_t hasBiasKey = CAUSAL_CONV1D_TPL_HAS_BIAS_FALSE;
uint32_t activationKey = CAUSAL_CONV1D_TPL_ACTIVATION_NONE;
bool hasBias = false;
bool hasCacheIndices = false;
bool hasInitialStateMode = false;
bool hasNumAcceptedTokens = false;
};
class CausalConv1dTiling {
public:
explicit CausalConv1dTiling(gert::TilingContext *context) : context_(context)
{
}
ge::graphStatus DoTiling();
private:
gert::TilingContext *context_;
const int32_t *queryStartLocData_ = nullptr;
CausalConv1dInputInfo inputInfo_;
uint64_t ubSize_ = 0;
uint32_t coreNum_ = 0;
uint64_t cacheLineSize_ = 0;
int64_t ubLimitedBaseDim_ = 0;
int64_t baseDim_ = 0;
int64_t coBatch_ = 1;
int64_t tokensPerBlock_ = 0;
int64_t tokenBlockCnt_ = 0;
ge::graphStatus GetPlatformInfo();
ge::graphStatus SetWorkspaceSize(size_t workspaceSize);
ge::graphStatus SetupFnInitStateWorkspace();
ge::graphStatus GetAttrsInfo(CausalConv1dAttrInfo &attrInfo);
ge::graphStatus ParseCausalConv1dInputInfo(const CausalConv1dAttrInfo &attrInfo);
ge::graphStatus SetTilingData(const CausalConv1dAttrInfo &attrInfo);
bool ChooseBaseDim(int64_t cuSeqlen, int64_t dim);
static constexpr bool IsFnInputMode(uint32_t inputModeKey);
static constexpr bool IsVarlenInputModeKey(uint32_t key)
{
return key == CAUSAL_CONV1D_TPL_INPUT_MODE_FN_VARLEN || key == CAUSAL_CONV1D_TPL_INPUT_MODE_UPDATE_VARLEN;
}
void ResolveTilingKeys(int64_t activationMode);
ge::graphStatus ValidateDimSize(int64_t dim, ge::DataType xDtype);
ge::graphStatus ParseXShape(bool isFnMode);
ge::graphStatus ParseWeightShape();
ge::graphStatus ParseConvStatesShape();
ge::graphStatus ParseQueryStartLoc(bool isFnMode);
ge::graphStatus ParseCacheIndices(int64_t nullBlockId);
ge::graphStatus ParseInitialStateMode(bool isFnMode);
ge::graphStatus ParseNumAcceptedTokens(bool isFnMode);
ge::graphStatus ParseBias();
bool InferIsFnMode();
int64_t ComputeUbLimitedBaseDim();
};
constexpr bool CausalConv1dTiling::IsFnInputMode(uint32_t inputModeKey)
{
return (inputModeKey == CAUSAL_CONV1D_TPL_INPUT_MODE_FN_VARLEN) ||
(inputModeKey == CAUSAL_CONV1D_TPL_INPUT_MODE_FN_BATCH);
}
void CausalConv1dTiling::ResolveTilingKeys(int64_t activationMode)
{
inputInfo_.widthKey = static_cast<uint32_t>(inputInfo_.kernelWidth);
inputInfo_.hasBiasKey = inputInfo_.hasBias ? CAUSAL_CONV1D_TPL_HAS_BIAS_TRUE : CAUSAL_CONV1D_TPL_HAS_BIAS_FALSE;
inputInfo_.activationKey = static_cast<uint32_t>(activationMode);
}
ge::graphStatus CausalConv1dTiling::GetPlatformInfo()
{
fe::PlatFormInfos *platformInfoPtr = context_->GetPlatformInfo();
OP_CHECK_NULL_WITH_CONTEXT(context_, platformInfoPtr);
auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfoPtr);
coreNum_ = ascendcPlatform.GetCoreNumAiv();
OP_CHECK_IF(coreNum_ == 0, OP_LOGE(context_, "coreNum_ is 0"), return ge::GRAPH_FAILED);
ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize_);
OP_CHECK_IF(ubSize_ == 0, OP_LOGE(context_, "ubSize_ is 0"), return ge::GRAPH_FAILED);
cacheLineSize_ = Ops::Base::GetCacheLineSize(context_);
OP_CHECK_IF(cacheLineSize_ == 0, OP_LOGE(context_, "cacheLineSize_ is 0"), return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::SetWorkspaceSize(size_t workspaceSize)
{
size_t *currentWorkspace = context_->GetWorkspaceSizes(1);
OP_CHECK_NULL_WITH_CONTEXT(context_, currentWorkspace);
currentWorkspace[0] = workspaceSize;
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::SetupFnInitStateWorkspace()
{
if (!IsFnInputMode(inputInfo_.inputModeKey) || !inputInfo_.hasInitialStateMode) {
return SetWorkspaceSize(0);
}
int64_t snapshotBytes = 0;
bool overflow = __builtin_mul_overflow(inputInfo_.numCacheLines, inputInfo_.stateLen, &snapshotBytes) ||
__builtin_mul_overflow(snapshotBytes, inputInfo_.dim, &snapshotBytes) ||
__builtin_mul_overflow(snapshotBytes, inputInfo_.elemBytes, &snapshotBytes);
OP_CHECK_IF(overflow || snapshotBytes < 0,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "workspace_size", "snapshot size",
"overflow detected"),
return ge::GRAPH_FAILED);
const size_t totalSize = static_cast<size_t>(ASCENDC_RESERVED_WORKSPACE_SIZE) + static_cast<size_t>(snapshotBytes);
OP_CHECK_IF(SetWorkspaceSize(totalSize) != ge::GRAPH_SUCCESS, OP_LOGE(context_, "SetWorkspaceSize error"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(context_->SetScheduleMode(1) != ge::GRAPH_SUCCESS, OP_LOGE(context_, "SetScheduleMode(1) error"),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
static int64_t ParseActivationModeStr(const char *str)
{
if (str == nullptr || std::strcmp(str, "") == 0 || std::strcmp(str, "silu") == 0) {
return 1;
}
if (std::strcmp(str, "none") == 0) {
return 0;
}
return -1;
}
ge::graphStatus CausalConv1dTiling::GetAttrsInfo(CausalConv1dAttrInfo &attrInfo)
{
constexpr int32_t kActivationModeAttrIndex = 0;
constexpr int32_t kNullBlockIdAttrIndex = 1;
auto attrs = context_->GetAttrs();
OP_CHECK_NULL_WITH_CONTEXT(context_, attrs);
const char *activationModeStr = attrs->GetAttrPointer<char>(kActivationModeAttrIndex);
OP_CHECK_NULL_WITH_CONTEXT(context_, activationModeStr);
attrInfo.activationMode = ParseActivationModeStr(activationModeStr);
OP_CHECK_IF(attrInfo.activationMode < 0,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "activation_mode", activationModeStr,
"only supports \"silu\" or \"none\""),
return ge::GRAPH_FAILED);
const int64_t *nullBlockIdPtr = attrs->GetAttrPointer<int64_t>(kNullBlockIdAttrIndex);
OP_CHECK_NULL_WITH_CONTEXT(context_, nullBlockIdPtr);
attrInfo.nullBlockId = *nullBlockIdPtr;
OP_LOGI(context_, "AttrsInfo: activationMode[%s->%ld], nullBlockId[%ld].", activationModeStr,
attrInfo.activationMode, attrInfo.nullBlockId);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ValidateDimSize(int64_t dim, ge::DataType xDtype)
{
const int64_t dtypeSize = static_cast<int64_t>(ge::GetSizeByDataType(xDtype));
OP_CHECK_IF((dim * dtypeSize) % DIM_ALIGN_BYTES != 0,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "dim", std::to_string(dim).c_str(),
"must be 32-byte aligned"),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseXShape(bool isFnMode)
{
auto xShapePtr = context_->GetInputShape(X_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, xShapePtr);
auto xShape = EnsureNotScalar(xShapePtr->GetStorageShape());
if (xShape.GetDimNum() == 2) {
if (!isFnMode) {
inputInfo_.inputModeKey = CAUSAL_CONV1D_TPL_INPUT_MODE_UPDATE_BATCH;
inputInfo_.batch = xShape.GetDim(0);
inputInfo_.dim = xShape.GetDim(1);
inputInfo_.seqLen = 1;
inputInfo_.cuSeqlen = inputInfo_.batch;
OP_CHECK_IF(
inputInfo_.batch <= 0 || inputInfo_.dim <= 0,
OP_LOGE_FOR_INVALID_SHAPE_WITH_REASON(
context_->GetNodeName(), "x",
("batch=" + std::to_string(inputInfo_.batch) + ", dim=" + std::to_string(inputInfo_.dim)).c_str(),
"for 2D decode mode, shape must be (cu_seqlen, dim)"),
return ge::GRAPH_FAILED);
} else {
inputInfo_.inputModeKey = CAUSAL_CONV1D_TPL_INPUT_MODE_FN_VARLEN;
inputInfo_.cuSeqlen = xShape.GetDim(0);
inputInfo_.dim = xShape.GetDim(1);
inputInfo_.seqLen = 0;
OP_CHECK_IF(inputInfo_.dim <= 0 || inputInfo_.cuSeqlen <= 0,
OP_LOGE_FOR_INVALID_SHAPE_WITH_REASON(context_->GetNodeName(), "x",
("dim=" + std::to_string(inputInfo_.dim) +
", cuSeqlen=" + std::to_string(inputInfo_.cuSeqlen))
.c_str(),
"for 2D varlen mode, shape must be (cu_seqlen, dim)"),
return ge::GRAPH_FAILED);
}
} else if (xShape.GetDimNum() == 3) {
inputInfo_.inputModeKey =
isFnMode ? CAUSAL_CONV1D_TPL_INPUT_MODE_FN_BATCH : CAUSAL_CONV1D_TPL_INPUT_MODE_UPDATE_BATCH;
inputInfo_.batch = xShape.GetDim(0);
inputInfo_.seqLen = xShape.GetDim(1);
inputInfo_.dim = xShape.GetDim(2);
OP_CHECK_IF(inputInfo_.batch <= 0 || inputInfo_.dim <= 0 || inputInfo_.seqLen <= 0,
OP_LOGE_FOR_INVALID_SHAPE_WITH_REASON(context_->GetNodeName(), "x",
("batch=" + std::to_string(inputInfo_.batch) +
", dim=" + std::to_string(inputInfo_.dim) +
", seqLen=" + std::to_string(inputInfo_.seqLen))
.c_str(),
"for 3D batch mode, shape must be (batch, seqlen, dim)"),
return ge::GRAPH_FAILED);
int64_t cuSeqlen = 0;
OP_CHECK_IF(__builtin_mul_overflow(inputInfo_.batch, inputInfo_.seqLen, &cuSeqlen),
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(
context_->GetNodeName(), "x",
("batch=" + std::to_string(inputInfo_.batch) + " * seqLen=" + std::to_string(inputInfo_.seqLen))
.c_str(),
"batch * seqLen overflow"),
return ge::GRAPH_FAILED);
inputInfo_.cuSeqlen = cuSeqlen;
} else {
OP_LOGE_FOR_INVALID_SHAPEDIM(context_->GetNodeName(), "x", std::to_string(xShape.GetDimNum()).c_str(),
"2D (cu_seqlen, dim) or 3D (batch, seqlen, dim)");
return ge::GRAPH_FAILED;
}
auto xDesc = context_->GetInputDesc(X_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, xDesc);
const ge::DataType xDtype = xDesc->GetDataType();
OP_CHECK_IF(
xDtype != ge::DT_BF16 && xDtype != ge::DT_FLOAT16,
OP_LOGE_FOR_INVALID_DTYPE(context_->GetNodeName(), "x", Ops::Base::ToString(xDtype).c_str(), "FLOAT16 or BF16"),
return ge::GRAPH_FAILED);
inputInfo_.elemBytes = static_cast<int64_t>(ge::GetSizeByDataType(xDtype));
OP_CHECK_IF(ValidateDimSize(inputInfo_.dim, xDtype) != ge::GRAPH_SUCCESS,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(
context_->GetNodeName(), "dim", std::to_string(inputInfo_.dim).c_str(), "must be 32-byte aligned"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(inputInfo_.cuSeqlen > static_cast<int64_t>(std::numeric_limits<int32_t>::max()),
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "cu_seqlen",
std::to_string(inputInfo_.cuSeqlen).c_str(),
"exceeds int32_t max"),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseWeightShape()
{
auto wShapePtr = context_->GetInputShape(WEIGHT_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, wShapePtr);
auto wShape = EnsureNotScalar(wShapePtr->GetStorageShape());
OP_CHECK_IF(wShape.GetDimNum() != 2,
OP_LOGE_FOR_INVALID_SHAPEDIM(context_->GetNodeName(), "weight",
std::to_string(wShape.GetDimNum()).c_str(),
"2 dimensions (kernelWidth, dim)"),
return ge::GRAPH_FAILED);
inputInfo_.kernelWidth = wShape.GetDim(0);
const int64_t wDim = wShape.GetDim(1);
OP_CHECK_IF(wDim != inputInfo_.dim,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "weight", std::to_string(wDim).c_str(),
("dim=" + std::to_string(inputInfo_.dim)).c_str()),
return ge::GRAPH_FAILED);
OP_CHECK_IF(inputInfo_.kernelWidth < 2 || inputInfo_.kernelWidth > 4,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "kernel_width",
std::to_string(inputInfo_.kernelWidth).c_str(),
"only supports [2, 3, 4]"),
return ge::GRAPH_FAILED);
auto wDesc = context_->GetInputDesc(WEIGHT_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, wDesc);
auto xDesc = context_->GetInputDesc(X_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, xDesc);
OP_CHECK_IF(
wDesc->GetDataType() != xDesc->GetDataType(),
OP_LOGE_FOR_INVALID_DTYPES_WITH_REASON(
context_->GetNodeName(), "weight and x",
(Ops::Base::ToString(wDesc->GetDataType()) + " and " + Ops::Base::ToString(xDesc->GetDataType())).c_str(),
"weight dtype must equal x dtype"),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseConvStatesShape()
{
auto convStatesShapePtr = context_->GetInputShape(CONV_STATES_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, convStatesShapePtr);
auto convStatesShape = EnsureNotScalar(convStatesShapePtr->GetStorageShape());
OP_CHECK_IF(convStatesShape.GetDimNum() != 3,
OP_LOGE_FOR_INVALID_SHAPEDIM(context_->GetNodeName(), "conv_states",
std::to_string(convStatesShape.GetDimNum()).c_str(),
"3D (num_cache_lines, state_len, dim)"),
return ge::GRAPH_FAILED);
inputInfo_.numCacheLines = convStatesShape.GetDim(0);
inputInfo_.stateLen = convStatesShape.GetDim(1);
const int64_t convStatesDim = convStatesShape.GetDim(2);
OP_CHECK_IF(inputInfo_.numCacheLines <= 0,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "conv_states",
std::to_string(inputInfo_.numCacheLines).c_str(), "num_cache_lines > 0"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(convStatesDim != inputInfo_.dim,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "conv_states",
std::to_string(convStatesDim).c_str(),
("dim=" + std::to_string(inputInfo_.dim)).c_str()),
return ge::GRAPH_FAILED);
OP_CHECK_IF(inputInfo_.stateLen < (inputInfo_.kernelWidth - 1),
OP_LOGE_FOR_INVALID_SHAPESIZE(
context_->GetNodeName(), "conv_states", std::to_string(inputInfo_.stateLen).c_str(),
("state_len >= kernelWidth-1=" + std::to_string(inputInfo_.kernelWidth - 1)).c_str()),
return ge::GRAPH_FAILED);
OP_CHECK_IF(inputInfo_.numCacheLines < inputInfo_.batch,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "conv_states",
std::to_string(inputInfo_.numCacheLines).c_str(),
("num_cache_lines >= batch=" + std::to_string(inputInfo_.batch)).c_str()),
return ge::GRAPH_FAILED);
auto convStatesDesc = context_->GetInputDesc(CONV_STATES_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, convStatesDesc);
auto xDesc = context_->GetInputDesc(X_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, xDesc);
OP_CHECK_IF(convStatesDesc->GetDataType() != xDesc->GetDataType(),
OP_LOGE_FOR_INVALID_DTYPES_WITH_REASON(context_->GetNodeName(), "conv_states and x",
(Ops::Base::ToString(convStatesDesc->GetDataType()) + " and " +
Ops::Base::ToString(xDesc->GetDataType()))
.c_str(),
"conv_states dtype must equal x dtype"),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseQueryStartLoc(bool isFnMode)
{
auto qslShapePtr = context_->GetOptionalInputShape(QUERY_START_LOC_INDEX);
const gert::CompileTimeTensorDesc *qslDesc = context_->GetOptionalInputDesc(QUERY_START_LOC_INDEX);
bool qslExist = false;
int64_t qslSize = 0;
if (qslShapePtr != nullptr) {
const auto qslStorageShape = qslShapePtr->GetStorageShape();
const int64_t qslDimNum = qslStorageShape.GetDimNum();
qslExist = !(qslDimNum == 0 || (qslDimNum == 1 && qslStorageShape.GetDim(0) <= 0));
if (qslExist) {
auto qslShape = EnsureNotScalar(qslStorageShape);
OP_CHECK_IF(qslShape.GetDimNum() != 1,
OP_LOGE_FOR_INVALID_SHAPEDIM(context_->GetNodeName(), "query_start_loc",
std::to_string(qslShape.GetDimNum()).c_str(), "1D"),
return ge::GRAPH_FAILED);
qslSize = qslShape.GetDim(0);
OP_CHECK_IF(qslSize < 1,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "query_start_loc",
std::to_string(qslSize).c_str(), "size >= 1"),
return ge::GRAPH_FAILED);
OP_CHECK_NULL_WITH_CONTEXT(context_, qslDesc);
OP_CHECK_IF(qslDesc->GetDataType() != ge::DT_INT32,
OP_LOGE_FOR_INVALID_DTYPE(context_->GetNodeName(), "query_start_loc",
Ops::Base::ToString(qslDesc->GetDataType()).c_str(), "int32"),
return ge::GRAPH_FAILED);
}
}
if (qslExist) {
if (!isFnMode && !IsVarlenInputModeKey(inputInfo_.inputModeKey) && (qslSize - 1) != inputInfo_.batch) {
inputInfo_.inputModeKey = CAUSAL_CONV1D_TPL_INPUT_MODE_UPDATE_VARLEN;
inputInfo_.seqLen = 0;
}
if (IsVarlenInputModeKey(inputInfo_.inputModeKey)) {
inputInfo_.batch = qslSize - 1;
} else {
OP_CHECK_IF(qslSize != inputInfo_.batch + 1,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "query_start_loc",
std::to_string(qslSize).c_str(),
("batch+1=" + std::to_string(inputInfo_.batch + 1)).c_str()),
return ge::GRAPH_FAILED);
}
} else {
OP_CHECK_IF(IsVarlenInputModeKey(inputInfo_.inputModeKey),
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "query_start_loc", "not provided",
"required in varlen mode"),
return ge::GRAPH_FAILED);
qslSize = inputInfo_.batch + 1;
}
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseCacheIndices(int64_t nullBlockId)
{
auto cIdxShapePtr = context_->GetOptionalInputShape(CACHE_INDICES_INDEX);
if (cIdxShapePtr == nullptr) {
return ge::GRAPH_SUCCESS;
}
const auto cIdxStorageShape = cIdxShapePtr->GetStorageShape();
const int64_t cIdxDimNum = cIdxStorageShape.GetDimNum();
inputInfo_.hasCacheIndices = (cIdxDimNum > 0) && (cIdxDimNum != 1 || cIdxStorageShape.GetDim(0) > 0);
if (!inputInfo_.hasCacheIndices) {
return ge::GRAPH_SUCCESS;
}
auto cIdxShape = EnsureNotScalar(cIdxStorageShape);
OP_CHECK_IF(cIdxShape.GetDimNum() != 1,
OP_LOGE_FOR_INVALID_SHAPEDIM(context_->GetNodeName(), "cache_indices",
std::to_string(cIdxShape.GetDimNum()).c_str(), "1D"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(cIdxShape.GetDim(0) != inputInfo_.batch,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "cache_indices",
std::to_string(cIdxShape.GetDim(0)).c_str(),
("batch=" + std::to_string(inputInfo_.batch)).c_str()),
return ge::GRAPH_FAILED);
auto cIdxDesc = context_->GetOptionalInputDesc(CACHE_INDICES_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, cIdxDesc);
OP_CHECK_IF(cIdxDesc->GetDataType() != ge::DT_INT32,
OP_LOGE_FOR_INVALID_DTYPE(context_->GetNodeName(), "cache_indices",
Ops::Base::ToString(cIdxDesc->GetDataType()).c_str(), "int32"),
return ge::GRAPH_FAILED);
const gert::Tensor *cIdxTensor = context_->GetOptionalInputTensor(CACHE_INDICES_INDEX);
if (cIdxTensor == nullptr) {
return ge::GRAPH_SUCCESS;
}
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseInitialStateMode(bool isFnMode)
{
auto initStShapePtr = context_->GetOptionalInputShape(INITIAL_STATE_MODE_INDEX);
if (initStShapePtr == nullptr) {
return ge::GRAPH_SUCCESS;
}
const auto initStStorageShape = initStShapePtr->GetStorageShape();
const int64_t initStDimNum = initStStorageShape.GetDimNum();
inputInfo_.hasInitialStateMode = (initStDimNum > 0) && (initStDimNum != 1 || initStStorageShape.GetDim(0) > 0);
if (!inputInfo_.hasInitialStateMode) {
return ge::GRAPH_SUCCESS;
}
OP_CHECK_IF(!isFnMode,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "initial_state_mode", "provided",
"only supported in prefill mode"),
return ge::GRAPH_FAILED);
auto initStShape = EnsureNotScalar(initStStorageShape);
OP_CHECK_IF(initStShape.GetDimNum() != 1,
OP_LOGE_FOR_INVALID_SHAPEDIM(context_->GetNodeName(), "initial_state_mode",
std::to_string(initStShape.GetDimNum()).c_str(), "1D"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(initStShape.GetDim(0) != inputInfo_.batch,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "initial_state_mode",
std::to_string(initStShape.GetDim(0)).c_str(),
("batch=" + std::to_string(inputInfo_.batch)).c_str()),
return ge::GRAPH_FAILED);
auto initStDesc = context_->GetOptionalInputDesc(INITIAL_STATE_MODE_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, initStDesc);
OP_CHECK_IF(initStDesc->GetDataType() != ge::DT_INT32,
OP_LOGE_FOR_INVALID_DTYPE(context_->GetNodeName(), "initial_state_mode",
Ops::Base::ToString(initStDesc->GetDataType()).c_str(), "int32"),
return ge::GRAPH_FAILED);
const gert::Tensor *initStTensor = context_->GetOptionalInputTensor(INITIAL_STATE_MODE_INDEX);
if (initStTensor == nullptr) {
return ge::GRAPH_SUCCESS;
}
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseNumAcceptedTokens(bool isFnMode)
{
auto numAccShapePtr = context_->GetOptionalInputShape(NUM_ACCEPTED_TOKENS_INDEX);
if (numAccShapePtr == nullptr) {
return ge::GRAPH_SUCCESS;
}
const auto numAccStorageShape = numAccShapePtr->GetStorageShape();
const int64_t numAccDimNum = numAccStorageShape.GetDimNum();
inputInfo_.hasNumAcceptedTokens = (numAccDimNum > 0) && (numAccDimNum != 1 || numAccStorageShape.GetDim(0) > 0);
if (!inputInfo_.hasNumAcceptedTokens) {
return ge::GRAPH_SUCCESS;
}
OP_CHECK_IF(isFnMode,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "num_accepted_tokens", "provided",
"only supported in decode/update mode"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(inputInfo_.kernelWidth != 4,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "kernel_width",
std::to_string(inputInfo_.kernelWidth).c_str(),
"num_accepted_tokens only supports kernelWidth=4"),
return ge::GRAPH_FAILED);
auto numAccShape = EnsureNotScalar(numAccStorageShape);
OP_CHECK_IF(numAccShape.GetDimNum() != 1,
OP_LOGE_FOR_INVALID_SHAPEDIM(context_->GetNodeName(), "num_accepted_tokens",
std::to_string(numAccShape.GetDimNum()).c_str(), "1D"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(numAccShape.GetDim(0) != inputInfo_.batch,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "num_accepted_tokens",
std::to_string(numAccShape.GetDim(0)).c_str(),
("batch=" + std::to_string(inputInfo_.batch)).c_str()),
return ge::GRAPH_FAILED);
if (!IsVarlenInputModeKey(inputInfo_.inputModeKey)) {
const int64_t reqStateLen = (inputInfo_.kernelWidth - 1) + (inputInfo_.seqLen - 1);
OP_CHECK_IF(
inputInfo_.stateLen < reqStateLen,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(
context_->GetNodeName(), "state_len", std::to_string(inputInfo_.stateLen).c_str(),
("spec decode requires stateLen >= (kernelWidth-1) + (seqlen-1) = " + std::to_string(reqStateLen))
.c_str()),
return ge::GRAPH_FAILED);
}
auto numAccDesc = context_->GetOptionalInputDesc(NUM_ACCEPTED_TOKENS_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, numAccDesc);
OP_CHECK_IF(numAccDesc->GetDataType() != ge::DT_INT32,
OP_LOGE_FOR_INVALID_DTYPE(context_->GetNodeName(), "num_accepted_tokens",
Ops::Base::ToString(numAccDesc->GetDataType()).c_str(), "int32"),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseBias()
{
auto biasShapePtr = context_->GetOptionalInputShape(BIAS_INDEX);
if (biasShapePtr == nullptr) {
return ge::GRAPH_SUCCESS;
}
const auto biasStorageShape = biasShapePtr->GetStorageShape();
const int64_t biasDimNum = biasStorageShape.GetDimNum();
inputInfo_.hasBias = (biasDimNum > 0) && (biasDimNum != 1 || biasStorageShape.GetDim(0) > 0);
if (!inputInfo_.hasBias) {
return ge::GRAPH_SUCCESS;
}
auto biasShape = EnsureNotScalar(biasStorageShape);
OP_CHECK_IF(biasShape.GetDimNum() != 1,
OP_LOGE_FOR_INVALID_SHAPEDIM(context_->GetNodeName(), "bias",
std::to_string(biasShape.GetDimNum()).c_str(), "1D (dim,)"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(biasShape.GetDim(0) != inputInfo_.dim,
OP_LOGE_FOR_INVALID_SHAPESIZE(context_->GetNodeName(), "bias",
std::to_string(biasShape.GetDim(0)).c_str(),
("dim=" + std::to_string(inputInfo_.dim)).c_str()),
return ge::GRAPH_FAILED);
auto biasDesc = context_->GetOptionalInputDesc(BIAS_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, biasDesc);
auto xDesc = context_->GetInputDesc(X_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context_, xDesc);
OP_CHECK_IF(biasDesc->GetDataType() != xDesc->GetDataType(),
OP_LOGE_FOR_INVALID_DTYPES_WITH_REASON(
context_->GetNodeName(), "bias and x",
(Ops::Base::ToString(biasDesc->GetDataType()) + " and " + Ops::Base::ToString(xDesc->GetDataType()))
.c_str(),
"bias dtype must equal x dtype"),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::ParseCausalConv1dInputInfo(const CausalConv1dAttrInfo &attrInfo)
{
const bool isFnMode = InferIsFnMode();
OP_CHECK_IF(ParseXShape(isFnMode) != ge::GRAPH_SUCCESS, OP_LOGE(context_, "ParseXShape error"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(ParseQueryStartLoc(isFnMode) != ge::GRAPH_SUCCESS, OP_LOGE(context_, "ParseQueryStartLoc error"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(ParseWeightShape() != ge::GRAPH_SUCCESS, OP_LOGE(context_, "ParseWeightShape error"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(ParseConvStatesShape() != ge::GRAPH_SUCCESS, OP_LOGE(context_, "ParseConvStatesShape error"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(ParseCacheIndices(attrInfo.nullBlockId) != ge::GRAPH_SUCCESS,
OP_LOGE(context_, "ParseCacheIndices error"), return ge::GRAPH_FAILED);
OP_CHECK_IF(ParseInitialStateMode(isFnMode) != ge::GRAPH_SUCCESS, OP_LOGE(context_, "ParseInitialStateMode error"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(ParseNumAcceptedTokens(isFnMode) != ge::GRAPH_SUCCESS,
OP_LOGE(context_, "ParseNumAcceptedTokens error"), return ge::GRAPH_FAILED);
OP_CHECK_IF(ParseBias() != ge::GRAPH_SUCCESS, OP_LOGE(context_, "ParseBias error"), return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
int64_t CausalConv1dTiling::ComputeUbLimitedBaseDim()
{
constexpr int64_t kOutBufCnt = 2;
const int64_t elemBytes = inputInfo_.elemBytes;
const int64_t alignElems = static_cast<int64_t>(cacheLineSize_) / elemBytes;
const int64_t ringBufCnt = inputInfo_.kernelWidth + 1;
const int64_t calcBufCnt = inputInfo_.kernelWidth + (inputInfo_.hasBias ? 1 : 0);
const int64_t bytesPerElem =
(ringBufCnt * elemBytes) + (kOutBufCnt * elemBytes) + (calcBufCnt * static_cast<int64_t>(sizeof(float)));
const int64_t rawBaseDim = static_cast<int64_t>(ubSize_) / bytesPerElem;
if (rawBaseDim <= 0) {
return 0;
}
return FloorPow2Multiple(rawBaseDim, alignElems);
}
bool CausalConv1dTiling::ChooseBaseDim(int64_t cuSeqlen, int64_t dim)
{
if (dim > ubLimitedBaseDim_) {
const int64_t alignElems = static_cast<int64_t>(cacheLineSize_) / inputInfo_.elemBytes;
int64_t bestBaseDim = alignElems;
int64_t maxActualCores = 0;
constexpr double kCoreUsageThresh = 0.8;
for (int64_t chunks = ubLimitedBaseDim_ / alignElems; chunks >= 1; chunks >>= 1) {
int64_t candidate = chunks * alignElems;
int64_t baseDimCnt = Ops::Base::CeilDiv(dim, candidate);
int64_t group = static_cast<int64_t>(coreNum_) / baseDimCnt;
if (group <= 0) {
continue;
}
int64_t tokenBlocks = Ops::Base::CeilDiv(cuSeqlen, Ops::Base::CeilDiv(cuSeqlen, group));
int64_t actualCores = tokenBlocks * baseDimCnt;
if (actualCores >= static_cast<int64_t>(static_cast<double>(coreNum_) * kCoreUsageThresh)) {
maxActualCores = actualCores;
bestBaseDim = candidate;
break;
}
if (actualCores > maxActualCores) {
maxActualCores = actualCores;
bestBaseDim = candidate;
}
}
baseDim_ = bestBaseDim;
coBatch_ = 1;
} else {
baseDim_ = dim;
int64_t maxCoBatch = ubLimitedBaseDim_ / dim;
if (inputInfo_.hasCacheIndices || inputInfo_.hasInitialStateMode || inputInfo_.hasNumAcceptedTokens ||
IsVarlenInputModeKey(inputInfo_.inputModeKey)) {
coBatch_ = 1;
} else {
coBatch_ = maxCoBatch;
while (coBatch_ > 1 && (inputInfo_.batch % coBatch_ != 0)) {
coBatch_--;
}
}
}
int64_t baseDimCnt = Ops::Base::CeilDiv(dim, baseDim_);
int64_t group = static_cast<int64_t>(coreNum_) / baseDimCnt;
if (group <= 0) {
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "dim", std::to_string(inputInfo_.dim).c_str(),
("too large for UB-limited baseDim: requires " +
std::to_string(baseDimCnt) + " cores, only " + std::to_string(coreNum_) +
" available")
.c_str());
return false;
}
tokensPerBlock_ = Ops::Base::CeilDiv(cuSeqlen, group);
if (coBatch_ > 1) {
int64_t alignedTokens = Ops::Base::CeilAlign(tokensPerBlock_, coBatch_);
int64_t origBlockCnt = Ops::Base::CeilDiv(cuSeqlen, tokensPerBlock_);
int64_t alignedBlockCnt = Ops::Base::CeilDiv(cuSeqlen, alignedTokens);
constexpr double kMinCoreRatio = 0.7;
if (static_cast<double>(alignedBlockCnt) >= static_cast<double>(origBlockCnt) * kMinCoreRatio) {
tokensPerBlock_ = alignedTokens;
} else {
coBatch_ = 1;
}
}
tokenBlockCnt_ = Ops::Base::CeilDiv(cuSeqlen, tokensPerBlock_);
return true;
}
}
using namespace CausalConv1d;
bool CausalConv1dTiling::InferIsFnMode()
{
auto xShapePtr = context_->GetInputShape(X_INDEX);
if (xShapePtr == nullptr) {
OP_LOGE(context_, "InferIsFnMode: x shape is null");
return true;
}
auto xShape = EnsureNotScalar(xShapePtr->GetStorageShape());
int64_t xDims = xShape.GetDimNum();
auto numAccShapePtr = context_->GetOptionalInputShape(NUM_ACCEPTED_TOKENS_INDEX);
if (numAccShapePtr != nullptr) {
auto numAccShape = EnsureNotScalar(numAccShapePtr->GetStorageShape());
if (numAccShape.GetDimNum() > 0 && numAccShape.GetDim(0) > 0) {
return false;
}
}
if (xDims == 3) {
int64_t seqLen = xShape.GetDim(1);
return seqLen > 1;
}
auto qslShapePtr = context_->GetOptionalInputShape(QUERY_START_LOC_INDEX);
if (qslShapePtr == nullptr) {
return false;
}
auto qslShape = EnsureNotScalar(qslShapePtr->GetStorageShape());
if (qslShape.GetDimNum() == 0 || qslShape.GetDim(0) == 0) {
return false;
}
return true;
}
ge::graphStatus CausalConv1dTiling::SetTilingData(const CausalConv1dAttrInfo &attrInfo)
{
CausalConv1dTilingData *tiling = context_->GetTilingData<CausalConv1dTilingData>();
OP_CHECK_NULL_WITH_CONTEXT(context_, tiling);
tiling->activationMode = attrInfo.activationMode;
tiling->nullBlockId = attrInfo.nullBlockId;
tiling->dim = inputInfo_.dim;
tiling->cuSeqlen = inputInfo_.cuSeqlen;
tiling->seqLen = inputInfo_.seqLen;
tiling->batch = inputInfo_.batch;
tiling->kernelWidth = inputInfo_.kernelWidth;
tiling->stateLen = inputInfo_.stateLen;
tiling->numCacheLines = inputInfo_.numCacheLines;
tiling->hasCacheIndices = inputInfo_.hasCacheIndices;
tiling->hasInitialStateMode = inputInfo_.hasInitialStateMode;
tiling->hasNumAcceptedTokens = inputInfo_.hasNumAcceptedTokens;
OP_CHECK_IF(baseDim_ > static_cast<int64_t>(std::numeric_limits<int32_t>::max()) || baseDim_ < 0,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "baseDim",
std::to_string(baseDim_).c_str(), "exceeds int32_t range"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(coBatch_ > static_cast<int64_t>(std::numeric_limits<int32_t>::max()) || coBatch_ < 0,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "coBatch",
std::to_string(coBatch_).c_str(), "exceeds int32_t range"),
return ge::GRAPH_FAILED);
tiling->baseDim = static_cast<int32_t>(baseDim_);
tiling->coBatch = static_cast<int32_t>(coBatch_);
tiling->hasInitStateWorkspace = inputInfo_.hasInitialStateMode;
tiling->tokensPerBlock = tokensPerBlock_;
tiling->tokenBlockCnt = tokenBlockCnt_;
OP_LOGI(context_,
"TilingData: dim[%ld], cuSeqlen[%ld], seqLen[%ld], batch[%ld], kernelWidth[%ld], "
"stateLen[%ld], numCacheLines[%ld], activationMode[%ld], nullBlockId[%ld], "
"baseDim[%d], coBatch[%d], tokensPerBlock[%ld], tokenBlockCnt[%ld], "
"hasNumAcceptedTokens[%d], hasCacheIndices[%d], hasInitialStateMode[%d], "
"hasInitStateWorkspace[%d], hasBias[%d].",
tiling->dim, tiling->cuSeqlen, tiling->seqLen, tiling->batch, tiling->kernelWidth, tiling->stateLen,
tiling->numCacheLines, tiling->activationMode, tiling->nullBlockId, tiling->baseDim, tiling->coBatch,
tiling->tokensPerBlock, tiling->tokenBlockCnt, static_cast<int32_t>(tiling->hasNumAcceptedTokens),
static_cast<int32_t>(tiling->hasCacheIndices), static_cast<int32_t>(tiling->hasInitialStateMode),
static_cast<int32_t>(tiling->hasInitStateWorkspace), static_cast<int32_t>(inputInfo_.hasBias));
return ge::GRAPH_SUCCESS;
}
ge::graphStatus CausalConv1dTiling::DoTiling()
{
OP_CHECK_IF(GetPlatformInfo() != ge::GRAPH_SUCCESS, OP_LOGE(context_, "GetPlatformInfo error"),
return ge::GRAPH_FAILED);
CausalConv1dAttrInfo attrInfo;
OP_CHECK_IF(GetAttrsInfo(attrInfo) != ge::GRAPH_SUCCESS, OP_LOGE(context_, "GetAttrsInfo error"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(ParseCausalConv1dInputInfo(attrInfo) != ge::GRAPH_SUCCESS,
OP_LOGE(context_, "ParseCausalConv1dInputInfo error"), return ge::GRAPH_FAILED);
ubLimitedBaseDim_ = ComputeUbLimitedBaseDim();
OP_CHECK_IF(ubLimitedBaseDim_ <= 0,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "ub_size",
std::to_string(ubSize_).c_str(),
"UB too small to allocate convolution buffers"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(!ChooseBaseDim(inputInfo_.cuSeqlen, inputInfo_.dim), OP_LOGE(context_, "ChooseBaseDim failed"),
return ge::GRAPH_FAILED);
ResolveTilingKeys(attrInfo.activationMode);
OP_CHECK_IF(SetupFnInitStateWorkspace() != ge::GRAPH_SUCCESS, OP_LOGE(context_, "SetupWorkspace error"),
return ge::GRAPH_FAILED);
OP_CHECK_IF(SetTilingData(attrInfo) != ge::GRAPH_SUCCESS, OP_LOGE(context_, "SetTilingData error"),
return ge::GRAPH_FAILED);
const int64_t blockDimVal = tokenBlockCnt_ * Ops::Base::CeilDiv(inputInfo_.dim, baseDim_);
OP_CHECK_IF(blockDimVal > static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) || blockDimVal < 0,
OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context_->GetNodeName(), "blockDim",
std::to_string(blockDimVal).c_str(), "exceeds uint32_t range"),
return ge::GRAPH_FAILED);
uint32_t blockDim = static_cast<uint32_t>(blockDimVal);
context_->SetBlockDim(blockDim);
const uint64_t tilingKey = GET_TPL_TILING_KEY(inputInfo_.inputModeKey, inputInfo_.widthKey, inputInfo_.hasBiasKey,
inputInfo_.activationKey);
context_->SetTilingKey(tilingKey);
return ge::GRAPH_SUCCESS;
}
static ge::graphStatus CausalConv1dTilingFunc(gert::TilingContext *context)
{
CausalConv1dTiling tctx(context);
return tctx.DoTiling();
}
static ge::graphStatus TilingParseForCausalConv1d(gert::TilingParseContext *context)
{
OP_LOGD(context, "Enter TilingParseForCausalConv1d.");
return ge::GRAPH_SUCCESS;
}
IMPL_OP_OPTILING(CausalConv1d)
.Tiling(CausalConv1dTilingFunc)
.TilingParse<CausalConv1dCompileInfo>(TilingParseForCausalConv1d);
}