Merged
【feat】: 支持子字节dtype(DT_INT4)的ND/FRACTAL_NZ格式转换与常量折叠防护 #5199
kantao1Add on Sep 28
【feat】: 支持子字节dtype(DT_INT4)的ND/FRACTAL_NZ格式转换与常量折叠防护 #5199
Merged
Merged fromkantao1/gesupport_onnx_int4intocann/gedevelop
Total: 18 files changed+1794-51
| @@ -12,8 +12,10 @@ | |||
| 12 | 12 | ||
| 13 | #include <securec.h> | 13 | #include <securec.h> |
| 14 | #include <memory> | 14 | #include <memory> |
| 15 | +#include <vector> | ||
| 15 | 16 | ||
| 16 | #include "graph_metadef/common/ge_common/util.h" | 17 | #include "graph_metadef/common/ge_common/util.h" |
| 18 | +#include "graph_metadef/graph/debug/ge_util.h" | ||
| 17 | #include "formats/utils/formats_definitions.h" | 19 | #include "formats/utils/formats_definitions.h" |
| 18 | #include "formats/utils/formats_trans_utils.h" | 20 | #include "formats/utils/formats_trans_utils.h" |
| 19 | #include "framework/common/debug/ge_log.h" | 21 | #include "framework/common/debug/ge_log.h" |
| @@ -88,6 +88,12 @@ bool IsTransposeArgValid(const uint8_t *const src, const std::vector<int64_t> &s | |||
| 88 | TypeUtils::DataTypeToSerialString(src_data_type).c_str()); | 88 | TypeUtils::DataTypeToSerialString(src_data_type).c_str()); |
| 89 | return false; | 89 | return false; |
| 90 | } | 90 | } |
| 91 | + // 子字节类型(如 DT_INT4)GetSizeByDataType 返回位宽编码值,逐元素字节拷贝不适用 | ||
| 92 | + if (GetSizeByDataType(src_data_type) > kDataTypeSizeBitOffset) { | ||
| 93 | + GELOGE(ACL_ERROR_GE_DATATYPE_INVALID, "[Trans][Param]Failed, the sub-byte data type %s is not supported", | ||
| 94 | + TypeUtils::DataTypeToSerialString(src_data_type).c_str()); | ||
| 95 | + return false; | ||
| 96 | + } | ||
| 91 | return IsShapeArgValid(src_shape, perm_arg); | 97 | return IsShapeArgValid(src_shape, perm_arg); |
| 92 | } | 98 | } |
| 93 | 99 | ||
| @@ -27,7 +27,72 @@ | |||
| 27 | 27 | ||
| 28 | namespace ge { | 28 | namespace ge { |
| 29 | namespace formats { | 29 | namespace formats { |
| 30 | +namespace { | ||
| 31 | +// Element-level format transfer indexes data by per-element byte size. | ||
| 32 | +// Sub-byte types (e.g. DT_INT4) encode bit-width in GetSizeByDataType (kDataTypeSizeBitOffset + bits), | ||
| 33 | +// which is not a real byte size, so they cannot be processed by existing transfers, | ||
| 34 | +// except the fractal NZ family transfers which handle packed data by bit-width. | ||
| 35 | +bool IsSubByteDataType(const DataType data_type) { | ||
| 36 | + return GetSizeByDataType(data_type) > kDataTypeSizeBitOffset; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +bool IsNdFamilyFormat(const Format format) { | ||
| 40 | + const auto primary = static_cast<Format>(GetPrimaryFormat(static_cast<int32_t>(format))); | ||
| 41 | + return (primary == FORMAT_ND) || (primary == FORMAT_NCHW) || (primary == FORMAT_NHWC); | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +bool IsFractalNzFamilyFormat(const Format format) { | ||
| 45 | + const auto primary = static_cast<Format>(GetPrimaryFormat(static_cast<int32_t>(format))); | ||
| 46 | + return (primary == FORMAT_FRACTAL_NZ) || (primary == FORMAT_FRACTAL_NZ_C0_2) || (primary == FORMAT_FRACTAL_NZ_C0_4) || | ||
| 47 | + (primary == FORMAT_FRACTAL_NZ_C0_8) || (primary == FORMAT_FRACTAL_NZ_C0_16) || | ||
| 48 | + (primary == FORMAT_FRACTAL_NZ_C0_32); | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +// 判断格式是否显式携带 C0:primary 枚举 C0 变体(如 FRACTAL_NZ_C0_16/C0_32)或组合位域 | ||
| 52 | +bool HasExplicitC0(const Format format) { | ||
| 53 | + if (GetC0Value(static_cast<int32_t>(format)) > 0) { | ||
| 54 | + return true; | ||
| 55 | + } | ||
| 56 | + const auto primary = static_cast<Format>(GetPrimaryFormat(static_cast<int32_t>(format))); | ||
| 57 | + return (primary == FORMAT_FRACTAL_NZ_C0_2) || (primary == FORMAT_FRACTAL_NZ_C0_4) || | ||
| 58 | + (primary == FORMAT_FRACTAL_NZ_C0_8) || (primary == FORMAT_FRACTAL_NZ_C0_16) || | ||
| 59 | + (primary == FORMAT_FRACTAL_NZ_C0_32); | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +bool IsSubByteSupportedTransfer(const TransArgs &args) { | ||
| 63 | + const bool nd_to_nz = IsNdFamilyFormat(args.src_format) && IsFractalNzFamilyFormat(args.dst_format); | ||
| 64 | + const bool nz_to_nd = IsFractalNzFamilyFormat(args.src_format) && IsNdFamilyFormat(args.dst_format); | ||
| 65 | + if (!nd_to_nz && !nz_to_nd) { | ||
| 66 | + return false; | ||
| 67 | + } | ||
| 68 | + // 子字节场景要求 NZ 侧显式携带 C0,裸 FORMAT_FRACTAL_NZ 无法确定 C0,直接拒绝 | ||
| 69 | + if (!HasExplicitC0(args.src_format) && !HasExplicitC0(args.dst_format)) { | ||
| 70 | + return false; | ||
| 71 | + } | ||
| 72 | + // 显式 C0 的 NZ 格式仅注册了与 FORMAT_ND 的互转,门禁与注册表口径保持一致, | ||
| 73 | + // 避免落入 BuildFormatTransfer 后报误导性 FORMAT_INVALID | ||
| 74 | + return (args.src_format == FORMAT_ND) || (args.dst_format == FORMAT_ND); | ||
| 75 | +} | ||
| 76 | +} // namespace | ||
| 77 | + | ||
| 30 | Status TransDataFormat(const TransArgs &args, TransResult &result) { | 78 | Status TransDataFormat(const TransArgs &args, TransResult &result) { |
| 79 | + if (IsSubByteDataType(args.src_data_type)) { | ||
| 80 | + // 子字节类型仅放行 ND/NCHW/NHWC 与 FRACTAL_NZ 族互转(packed 位级转换),其余拒绝 | ||
| 81 | + GELOGI("TransDataFormat sub-byte dtype %s, src format %s -> dst format %s, supported transfer %d", | ||
| 82 | + TypeUtils::DataTypeToSerialString(args.src_data_type).c_str(), | ||
| 83 | + TypeUtils::FormatToSerialString(args.src_format).c_str(), | ||
| 84 | + TypeUtils::FormatToSerialString(args.dst_format).c_str(), IsSubByteSupportedTransfer(args) ? 1 : 0); | ||
| 85 | + if (!IsSubByteSupportedTransfer(args)) { | ||
| 86 | + const std::string error = "Failed to trans data format, sub-byte data type " + | ||
| 87 | + FmtToStr(TypeUtils::DataTypeToSerialString(args.src_data_type)) + " transfer from " + | ||
| 88 | + TypeUtils::FormatToSerialString(args.src_format) + " to " + | ||
| 89 | + TypeUtils::FormatToSerialString(args.dst_format) + | ||
| 90 | + " is not supported, only nd <-> fractal nz with explicit c0 is supported"; | ||
| 91 | + GE_ERRORLOG_AND_ERRORMSG(ACL_ERROR_GE_DATATYPE_INVALID, error.c_str()); | ||
| 92 | + return ACL_ERROR_GE_DATATYPE_INVALID; | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + | ||
| 31 | const auto transfer = BuildFormatTransfer(args); | 96 | const auto transfer = BuildFormatTransfer(args); |
| 32 | if (transfer == nullptr) { | 97 | if (transfer == nullptr) { |
| 33 | const std::string error = "Failed to trans data from format " + | 98 | const std::string error = "Failed to trans data from format " + |
| @@ -160,7 +160,8 @@ int GetDataTypeBits(const ge::DataType data_type) { | |||
| 160 | if (data_type_size <= 0) { | 160 | if (data_type_size <= 0) { |
| 161 | return data_type_size; | 161 | return data_type_size; |
| 162 | } | 162 | } |
| 163 | - if (data_type > ge::kDataTypeSizeBitOffset) { | 163 | + // 子字节类型(如 DT_INT4)GetSizeByDataType 返回位宽编码值(kDataTypeSizeBitOffset + bits) |
| 164 | + if (data_type_size > ge::kDataTypeSizeBitOffset) { | ||
| 164 | return data_type_size - ge::kDataTypeSizeBitOffset; | 165 | return data_type_size - ge::kDataTypeSizeBitOffset; |
| 165 | } | 166 | } |
| 166 | return data_type_size * ge::kBitNumOfOneByte; | 167 | return data_type_size * ge::kBitNumOfOneByte; |
| @@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | #include <memory> | 13 | #include <memory> |
| 14 | #include <new> | 14 | #include <new> |
| 15 | +#include <set> | ||
| 15 | #include <utility> | 16 | #include <utility> |
| 16 | #include <vector> | 17 | #include <vector> |
| 17 | #include "graph_metadef/common/ge_common/util.h" | 18 | #include "graph_metadef/common/ge_common/util.h" |
| @@ -28,8 +29,10 @@ | |||
| 28 | #include "graph/ir_definitions_recover.h" | 29 | #include "graph/ir_definitions_recover.h" |
| 29 | #include "graph/op_desc.h" | 30 | #include "graph/op_desc.h" |
| 30 | #include "graph/operator_factory.h" | 31 | #include "graph/operator_factory.h" |
| 32 | +#include "graph/types.h" | ||
| 31 | #include "graph/utils/node_utils.h" | 33 | #include "graph/utils/node_utils.h" |
| 32 | #include "graph/utils/constant_utils.h" | 34 | #include "graph/utils/constant_utils.h" |
| 35 | +#include "graph/utils/type_utils.h" | ||
| 33 | #include "host_cpu_engine/host_cpu_engine.h" | 36 | #include "host_cpu_engine/host_cpu_engine.h" |
| 34 | #include "api/gelib/gelib.h" | 37 | #include "api/gelib/gelib.h" |
| 35 | #include "register/op_kernel_registry.h" | 38 | #include "register/op_kernel_registry.h" |
| @@ -40,6 +43,41 @@ namespace { | |||
| 40 | const int64_t kStartCallNum = 1; | 43 | const int64_t kStartCallNum = 1; |
| 41 | const int64_t kShapeCalNum = 8; | 44 | const int64_t kShapeCalNum = 8; |
| 42 | const char *const kKernelLibName = "aicpu_ascend_kernel"; | 45 | const char *const kKernelLibName = "aicpu_ascend_kernel"; |
| 46 | + | ||
| 47 | +// 子字节类型(如 DT_INT4)GetSizeByDataType 返回位宽编码值(kDataTypeSizeBitOffset + bits),并非真实字节数 | ||
| 48 | +bool IsSubByteDtype(const DataType dtype) { | ||
| 49 | + return GetSizeByDataType(dtype) > kDataTypeSizeBitOffset; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +// 子字节 dtype HostCpuOp 折叠白名单:已在 Compute 中实现 packed 位级处理的算子 | ||
| 53 | +// (如 QuantBiasOptimization 的 INT4 packed→int8 解包)可安全折叠,放行输入检查 | ||
| 54 | +const std::set<std::string> kSubByteFoldWhiteList = {"QuantBiasOptimization"}; | ||
| 55 | + | ||
| 56 | +// 子字节类型(如 DT_INT4)的数据为 packed 存储(2元素/字节),需按位读写处理; | ||
| 57 | +// HostCpuOp/自定义算子实现可能缺少位级处理导致越界写,需拦截并回落 GE 内置 kernel | ||
| 58 | +// (TransdataKernel 等已适配子字节位级转换)。 | ||
| 59 | +// 覆盖 top/bottom 优先级自定义算子与 legacy HostCpuOp 全部路径; | ||
| 60 | +// 白名单内的算子已在 Compute 中实现子字节 packed 位级处理(如 QuantBiasOptimization),放行输入检查 | ||
| 61 | +bool IsSubByteFoldingBlocked(const std::string &op_type, const NodePtr &node, | ||
| 62 | + const std::vector<ConstGeTensorPtr> &inputs) { | ||
| 63 | + if (kSubByteFoldWhiteList.count(op_type) == 0U) { | ||
| 64 | + for (const auto &input : inputs) { | ||
| 65 | + if ((input != nullptr) && IsSubByteDtype(input->GetTensorDesc().GetDataType())) { | ||
| 66 | + GELOGI("Sub-byte dtype %s folding skips HostCpuOp for op %s, fallback to built-in kernel.", | ||
| 67 | + TypeUtils::DataTypeToSerialString(input->GetTensorDesc().GetDataType()).c_str(), op_type.c_str()); | ||
| 68 | + return true; | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | + for (const auto &output_desc : node->GetOpDesc()->GetAllOutputsDesc()) { | ||
| 73 | + if (IsSubByteDtype(output_desc.GetDataType())) { | ||
| 74 | + GELOGI("Sub-byte dtype %s folding skips HostCpuOp for op %s, fallback to built-in kernel.", | ||
| 75 | + TypeUtils::DataTypeToSerialString(output_desc.GetDataType()).c_str(), op_type.c_str()); | ||
| 76 | + return true; | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | + return false; | ||
| 80 | +} | ||
| 43 | const char *const kOpsFlagClose = "0"; | 81 | const char *const kOpsFlagClose = "0"; |
| 44 | const char *const kPassName = "ConstantFoldingPass"; | 82 | const char *const kPassName = "ConstantFoldingPass"; |
| 45 | 83 | ||
| @@ -332,6 +370,9 @@ Status ConstantFoldingPass::ComputeWithHostCpuCustomOp(const NodePtr &node, cons | |||
| 332 | std::vector<GeTensorPtr> &outputs) { | 370 | std::vector<GeTensorPtr> &outputs) { |
| 333 | const std::string op_type = NodeUtils::GetNodeType(node); | 371 | const std::string op_type = NodeUtils::GetNodeType(node); |
| 334 | const AscendString op_type_str(op_type.c_str()); | 372 | const AscendString op_type_str(op_type.c_str()); |
| 373 | + if (IsSubByteFoldingBlocked(op_type, node, inputs)) { | ||
| 374 | + return UNSUPPORTED; | ||
| 375 | + } | ||
| 335 | if (!CustomOpFactory::IsExistOp(op_type_str, OpBackend::kHostCPU)) { | 376 | if (!CustomOpFactory::IsExistOp(op_type_str, OpBackend::kHostCPU)) { |
| 336 | GELOGD("Op of type %s is not supported by host cpu custom op.", op_type.c_str()); | 377 | GELOGD("Op of type %s is not supported by host cpu custom op.", op_type.c_str()); |
| 337 | return UNSUPPORTED; | 378 | return UNSUPPORTED; |
| @@ -385,6 +426,9 @@ Status ConstantFoldingPass::RunOpKernel(const NodePtr &node, const std::vector<C | |||
| 385 | std::vector<GeTensorPtr> &outputs) { | 426 | std::vector<GeTensorPtr> &outputs) { |
| 386 | const std::string op_type = NodeUtils::GetNodeType(node); | 427 | const std::string op_type = NodeUtils::GetNodeType(node); |
| 387 | const AscendString op_type_str(op_type.c_str()); | 428 | const AscendString op_type_str(op_type.c_str()); |
| 429 | + if (IsSubByteFoldingBlocked(op_type, node, inputs)) { | ||
| 430 | + return UNSUPPORTED; | ||
| 431 | + } | ||
| 388 | if (CustomOpFactory::IsExistOp(op_type_str, OpBackend::kHostCPU, OpRegistrationPriority::kBottom, | 432 | if (CustomOpFactory::IsExistOp(op_type_str, OpBackend::kHostCPU, OpRegistrationPriority::kBottom, |
| 389 | OpEngine::kHostCpu)) { | 433 | OpEngine::kHostCpu)) { |
| 390 | GELOGD("Bottom priority host cpu op is registered. op type = %s, engine = %s.", op_type.c_str(), "HOST_CPU"); | 434 | GELOGD("Bottom priority host cpu op is registered. op type = %s, engine = %s.", op_type.c_str(), "HOST_CPU"); |
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <climits> | 15 | #include <climits> |
| 16 | 16 | ||
| 17 | #include "framework/common/framework_types_internal.h" | 17 | #include "framework/common/framework_types_internal.h" |
| 18 | +#include "graph/types.h" | ||
| 18 | #include "graph/utils/op_desc_utils.h" | 19 | #include "graph/utils/op_desc_utils.h" |
| 19 | #include "graph/utils/tensor_utils.h" | 20 | #include "graph/utils/tensor_utils.h" |
| 20 | #include "graph/utils/type_utils.h" | 21 | #include "graph/utils/type_utils.h" |
| @@ -177,12 +178,6 @@ bool KernelUtils::CheckSizeForTransOp(const ge::ConstGeTensorPtr &const_weight_p | |||
| 177 | return false; | 178 | return false; |
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | - uint32_t length = 1U; | ||
| 181 | - if (!TypeUtils::GetDataTypeLength(data_type, length)) { | ||
| 182 | - GELOGE(PARAM_INVALID, "Input datatype %d is not supported.", data_type); | ||
| 183 | - return false; | ||
| 184 | - } | ||
| 185 | - | ||
| 186 | GELOGI("Const real value Size:%zu, op_desc Shape Size:%ld, data_type:%s.", data_size, cal_size, | 181 | GELOGI("Const real value Size:%zu, op_desc Shape Size:%ld, data_type:%s.", data_size, cal_size, |
| 187 | TypeUtils::DataTypeToSerialString(data_type).c_str()); | 182 | TypeUtils::DataTypeToSerialString(data_type).c_str()); |
| 188 | if (shape_size != 0) { | 183 | if (shape_size != 0) { |
| @@ -199,7 +194,9 @@ bool KernelUtils::CheckSizeForTransOp(const ge::ConstGeTensorPtr &const_weight_p | |||
| 199 | } | 194 | } |
| 200 | } else { | 195 | } else { |
| 201 | // Scalar tensor, has only one element in tensor | 196 | // Scalar tensor, has only one element in tensor |
| 202 | - if ((length != 0U) && ((data_size / static_cast<size_t>(length)) != 1U)) { | 197 | + // 子字节类型(如 DT_INT4)按位宽 CeilDiv 得到单元素 packed 字节数 |
| 198 | + const int64_t expect_scalar_size = ge::GetSizeInBytes(1, data_type); | ||
| 199 | + if ((expect_scalar_size <= 0) || (data_size != static_cast<size_t>(expect_scalar_size))) { | ||
| 203 | GELOGW("Const input data size is not equal with tensor desc shape"); | 200 | GELOGW("Const input data size is not equal with tensor desc shape"); |
| 204 | return false; | 201 | return false; |
| 205 | } | 202 | } |
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "framework/common/op/ge_op_utils.h" | 19 | #include "framework/common/op/ge_op_utils.h" |
| 20 | #include "framework/common/debug/ge_log.h" | 20 | #include "framework/common/debug/ge_log.h" |
| 21 | #include "graph/debug/ge_attr_define.h" | 21 | #include "graph/debug/ge_attr_define.h" |
| 22 | +#include "graph/types.h" | ||
| 22 | #include "host_kernels/kernel_utils.h" | 23 | #include "host_kernels/kernel_utils.h" |
| 23 | #include "graph/utils/type_utils.h" | 24 | #include "graph/utils/type_utils.h" |
| 24 | #include "host_kernels/kernel_factory.h" | 25 | #include "host_kernels/kernel_factory.h" |
| @@ -78,6 +79,12 @@ Status PackKernel::ValidateKernelParams(const ge::OpDescPtr &op_desc_ptr, | |||
| 78 | return PARAM_INVALID; | 79 | return PARAM_INVALID; |
| 79 | } | 80 | } |
| 80 | data_type_ = op_desc_ptr->GetInputDesc(0).GetDataType(); | 81 | data_type_ = op_desc_ptr->GetInputDesc(0).GetDataType(); |
| 82 | + // 子字节类型(如 DT_INT4)的 Pack 需要位级打包,逐元素字节拷贝逻辑不适用,安全跳过折叠 | ||
| 83 | + if (GetSizeByDataType(data_type_) > kDataTypeSizeBitOffset) { | ||
| 84 | + GELOGI("Pack kernel skips sub-byte data type %s, ignore folding.", | ||
| 85 | + TypeUtils::DataTypeToSerialString(data_type_).c_str()); | ||
| 86 | + return NOT_CHANGED; | ||
| 87 | + } | ||
| 81 | GeShape shape = op_desc_ptr->GetInputDesc(0).GetShape(); | 88 | GeShape shape = op_desc_ptr->GetInputDesc(0).GetShape(); |
| 82 | if (axis_ < 0 || axis_ > (static_cast<int64_t>(shape.GetDimNum()) + 1)) { | 89 | if (axis_ < 0 || axis_ > (static_cast<int64_t>(shape.GetDimNum()) + 1)) { |
| 83 | GELOGW("Axis is %ld ,which is out of range [0,R+1].", axis_); | 90 | GELOGW("Axis is %ld ,which is out of range [0,R+1].", axis_); |
| @@ -70,9 +70,11 @@ Tensor *EagerOpExecutionContext::MallocOutputTensor(size_t index, const StorageS | |||
| 70 | GE_ASSERT_NOTNULL(output_tensor); | 70 | GE_ASSERT_NOTNULL(output_tensor); |
| 71 | 71 | ||
| 72 | SetTensorDesc(shape, format, dtype, output_tensor); | 72 | SetTensorDesc(shape, format, dtype, output_tensor); |
| 73 | - const size_t tensor_size = shape.GetStorageShape().GetShapeSize() * GetSizeByDataType(dtype); | 73 | + // 子字节类型(如 DT_INT4)按位宽 CeilDiv 换算 packed 字节数 |
| 74 | - size_t aligned_tensor_size = tensor_size; | 74 | + const int64_t byte_size = ge::GetSizeInBytes(shape.GetStorageShape().GetShapeSize(), dtype); |
| 75 | - GE_ASSERT_TRUE(!ge::RoundUpOverflow(tensor_size, kMemAlignment, aligned_tensor_size)); | 75 | + GE_ASSERT_TRUE(byte_size >= 0, "Get tensor size in bytes failed, dtype: %d", static_cast<int>(dtype)); |
| 76 | + size_t aligned_tensor_size = 0U; | ||
| 77 | + GE_ASSERT_TRUE(!ge::RoundUpOverflow(static_cast<size_t>(byte_size), kMemAlignment, aligned_tensor_size)); | ||
| 76 | const TensorData &tensor_data = output_tensor->GetTensorData(); | 78 | const TensorData &tensor_data = output_tensor->GetTensorData(); |
| 77 | // 静态场景下内存地址已赋值 不需要去Malloc | 79 | // 静态场景下内存地址已赋值 不需要去Malloc |
| 78 | // 注意:静态图逻辑地址可能是合法的0(偏移),不能用 GetAddr() != nullptr 判断 | 80 | // 注意:静态图逻辑地址可能是合法的0(偏移),不能用 GetAddr() != nullptr 判断 |
| @@ -1274,7 +1274,9 @@ bool TransferShapeUtils::GetNdToNzAlignedShape(const AlignShapeInfo &align_shape | |||
| 1274 | (void)aligned_shape.SetDim(i, 1); | 1274 | (void)aligned_shape.SetDim(i, 1); |
| 1275 | } | 1275 | } |
| 1276 | int64_t m0 = GetM0ByDtype(align_shape_info.data_type); | 1276 | int64_t m0 = GetM0ByDtype(align_shape_info.data_type); |
| 1277 | - int64_t c0 = GetC0ByDtype(align_shape_info.data_type); | 1277 | + // 统一 C0 解析口径:primary 枚举 C0 变体(如 FRACTAL_NZ_C0_16/C0_32)、组合位域、dtype 回退, |
| 1278 | + // 与格式转换侧(GetEffectiveC0)保持一致 | ||
| 1279 | + const int64_t c0 = GetC0Value(align_shape_info.data_type, align_shape_info.dst_format); | ||
| 1278 | (void)aligned_shape.SetDim(target_index, m0); | 1280 | (void)aligned_shape.SetDim(target_index, m0); |
| 1279 | (void)aligned_shape.SetDim(target_index + 1, c0); | 1281 | (void)aligned_shape.SetDim(target_index + 1, c0); |
| 1280 | return true; | 1282 | return true; |
| @@ -46,7 +46,13 @@ struct WeightInfo { | |||
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | if ((shape_size > 0) && (datatype < data_type_size.size())) { | 48 | if ((shape_size > 0) && (datatype < data_type_size.size())) { |
| 49 | - total_data_size = (static_cast<size_t>(shape_size)) * data_type_size[datatype]; | 49 | + // 子字节类型(如 DT_INT4)按位宽 CeilDiv 换算 packed 字节数,否则高估导致越界写 |
| 50 | + if (ge::GetSizeByDataType(datatype) > ge::kDataTypeSizeBitOffset) { | ||
| 51 | + const int64_t byte_size = ge::GetSizeInBytes(shape_size, datatype); | ||
| 52 | + total_data_size = (byte_size > 0) ? static_cast<size_t>(byte_size) : 0U; | ||
| 53 | + } else { | ||
| 54 | + total_data_size = (static_cast<size_t>(shape_size)) * data_type_size[datatype]; | ||
| 55 | + } | ||
| 50 | } else { | 56 | } else { |
| 51 | total_data_size = 0; | 57 | total_data_size = 0; |
| 52 | } | 58 | } |
| @@ -353,6 +353,7 @@ target_link_libraries(ge_common_atc | |||
| 353 | add_executable(graph_engine_compile_test | 353 | add_executable(graph_engine_compile_test |
| 354 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_main.cc" | 354 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_main.cc" |
| 355 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_file_constant.cc" | 355 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_file_constant.cc" |
| 356 | + "${AIR_CODE_DIR}/tests/ge/st/testcase/test_sub_byte_format.cc" | ||
| 356 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_data_slice.cc" | 357 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_data_slice.cc" |
| 357 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_swap_pass.cc" | 358 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_swap_pass.cc" |
| 358 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_mem_layout_conflict.cc" | 359 | "${AIR_CODE_DIR}/tests/ge/st/testcase/test_mem_layout_conflict.cc" |
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <graph/operator_reg.h> | 14 | #include <graph/operator_reg.h> |
| 15 | #include <ge_running_env/fake_op.h> | 15 | #include <ge_running_env/fake_op.h> |
| 16 | #include <ge_running_env/ge_running_env_faker.h> | 16 | #include <ge_running_env/ge_running_env_faker.h> |
| 17 | +#include <ge_running_env/tensor_utils.h> | ||
| 17 | 18 | ||
| 18 | #include "ge/ge_api.h" | 19 | #include "ge/ge_api.h" |
| 19 | #include "graph/debug/ge_attr_define.h" | 20 | #include "graph/debug/ge_attr_define.h" |
| @@ -154,7 +155,8 @@ class ConstantFoldingTest : public testing::Test { | |||
| 154 | .Install(FakeOp(SQUEEZEV3).InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)) | 155 | .Install(FakeOp(SQUEEZEV3).InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)) |
| 155 | .Install(FakeOp(ClipByValue).InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)) | 156 | .Install(FakeOp(ClipByValue).InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)) |
| 156 | .Install(FakeOp(CAST).InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)) | 157 | .Install(FakeOp(CAST).InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)) |
| 157 | - .Install(FakeOp("FakeWhere").InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)); | 158 | + .Install(FakeOp("FakeWhere").InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)) |
| 159 | + .Install(FakeOp(TRANSDATA).InfoStoreAndBuilder("DNN_VM_GE_LOCAL_OP_STORE").InferShape(StubInferFunction)); | ||
| 158 | } | 160 | } |
| 159 | void TearDown() { | 161 | void TearDown() { |
| 160 | std::cout << "End constant folding st" << std::endl; | 162 | std::cout << "End constant folding st" << std::endl; |
| @@ -811,3 +813,26 @@ TEST_F(ConstantFoldingTest, HostCpuCustomOpConstantFoldingCopiesTensor) { | |||
| 811 | }; | 813 | }; |
| 812 | CustomOpFactory::RemoveCustomOps({op_type}); | 814 | CustomOpFactory::RemoveCustomOps({op_type}); |
| 813 | } | 815 | } |
| 816 | + | ||
| 817 | +// INT4 constant weight folded via TransData (ND -> FRACTAL_NZ_C0_16), | ||
| 818 | +// covering the sub-byte (packed) format transfer chain in ST. | ||
| 819 | +TEST_F(ConstantFoldingTest, test_int4_transdata_folding) { | ||
| 820 | + Graph graph; | ||
| 821 | + { | ||
| 822 | + auto weight = GenerateTensor(DT_INT4, {2, 32}); | ||
| 823 | + auto transdata = OP_CFG(TRANSDATA).TensorDesc(FORMAT_FRACTAL_NZ_C0_16, DT_INT4, {1, 2, 1, 16, 16}); | ||
| 824 | + auto netoutput = OP_CFG(NETOUTPUT).TensorDesc(FORMAT_FRACTAL_NZ_C0_16, DT_INT4, {1, 2, 1, 16, 16}); | ||
| 825 | + auto constant = OP_CFG(CONSTANT).Weight(weight).TensorDesc(FORMAT_ND, DT_INT4, {2, 32}); | ||
| 826 | + DEF_GRAPH(g1) { | ||
| 827 | + CHAIN(NODE("int4_const", constant)->EDGE(0, 0)->NODE("int4_transdata", transdata)); | ||
| 828 | + CHAIN(NODE("int4_transdata", transdata)->EDGE(0, 0)->NODE("netoutput", netoutput)); | ||
| 829 | + }; | ||
| 830 | + graph = ToGeGraph(g1); | ||
| 831 | + } | ||
| 832 | + map<AscendString, AscendString> options; | ||
| 833 | + Session session(options); | ||
| 834 | + session.AddGraph(1, graph, options); | ||
| 835 | + std::vector<InputTensorInfo> inputs; | ||
| 836 | + auto ret = session.BuildGraph(1, inputs); | ||
| 837 | + EXPECT_EQ(ret, SUCCESS); | ||
| 838 | +} | ||
| @@ -0,0 +1,561 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include <gtest/gtest.h> | ||
| 12 | +#include <map> | ||
| 13 | +#include <vector> | ||
| 14 | + | ||
| 15 | +#include "formats/format_transfers/format_transfer_fractal_nz.h" | ||
| 16 | +#include "formats/format_transfers/format_transfer_transpose.h" | ||
| 17 | +#include "formats/formats.h" | ||
| 18 | +#include "transfer_shape_utils.h" | ||
| 19 | +#include "framework/common/ge_inner_error_codes.h" | ||
| 20 | +#include "graph/types.h" | ||
| 21 | +#include "graph/utils/type_utils.h" | ||
| 22 | +#include "host_kernels/kernel.h" | ||
| 23 | +#include "host_kernels/kernel_factory.h" | ||
| 24 | +#include "host_kernels/kernel_utils.h" | ||
| 25 | +#include "register/graph_optimizer/fusion_common/fusion_turbo.h" | ||
| 26 | +#include "register/op_kernel_registry.h" | ||
| 27 | + | ||
| 28 | +using namespace std; | ||
| 29 | +using namespace ge; | ||
| 30 | + | ||
| 31 | +namespace { | ||
| 32 | +uint8_t MakeInt4Byte(const uint8_t lo, const uint8_t hi) { | ||
| 33 | + return static_cast<uint8_t>((lo & 0x0FU) | static_cast<uint8_t>((hi & 0x0FU) << 4)); | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +std::vector<uint8_t> MakeInt4PackedData(const size_t elem_count) { | ||
| 37 | + std::vector<uint8_t> data((elem_count + 1U) / 2U, 0); | ||
| 38 | + for (size_t i = 0U; i < elem_count; ++i) { | ||
| 39 | + const auto value = static_cast<uint8_t>(i % 16U); | ||
| 40 | + if ((i % 2U) == 0U) { | ||
| 41 | + data[i / 2U] = MakeInt4Byte(value, data[i / 2U] >> 4); | ||
| 42 | + } else { | ||
| 43 | + data[i / 2U] = MakeInt4Byte(data[i / 2U] & 0x0FU, value); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + return data; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +uint8_t GetInt4Elem(const uint8_t *const data, const size_t elem_idx) { | ||
| 50 | + const uint8_t byte = data[elem_idx / 2U]; | ||
| 51 | + return ((elem_idx % 2U) == 0U) ? static_cast<uint8_t>(byte & 0x0FU) : static_cast<uint8_t>(byte >> 4); | ||
| 52 | +} | ||
| 53 | +} // namespace | ||
| 54 | + | ||
| 55 | +namespace ge { | ||
| 56 | +namespace formats { | ||
| 57 | +class StestSubByteFormat : public testing::Test { | ||
| 58 | + protected: | ||
| 59 | + void SetUp() {} | ||
| 60 | + void TearDown() {} | ||
| 61 | +}; | ||
| 62 | + | ||
| 63 | +// ND -> NZ (sub-byte): W aligned to C0, layout check + padding | ||
| 64 | +TEST_F(StestSubByteFormat, nd_to_nz_c0_16_success) { | ||
| 65 | + const std::vector<uint8_t> src = MakeInt4PackedData(2U * 32U); | ||
| 66 | + const Format src_format = static_cast<Format>(GetFormatFromSubAndC0(FORMAT_ND, FORMAT_RESERVED, 5)); | ||
| 67 | + const Format dst_format = static_cast<Format>(GetFormatFromSubAndC0(FORMAT_FRACTAL_NZ, FORMAT_RESERVED, 5)); | ||
| 68 | + TransArgs args{src.data(), src_format, dst_format, FORMAT_ND, FORMAT_FRACTAL_NZ, FORMAT_RESERVED, | ||
| 69 | + FORMAT_RESERVED, 16, 16, {2, 32}, {1, 2, 1, 16, 16}, DT_INT4}; | ||
| 70 | + TransResult result; | ||
| 71 | + FormatTransferFractalNz transfer; | ||
| 72 | + ASSERT_EQ(transfer.TransFormat(args, result), SUCCESS); | ||
| 73 | + EXPECT_EQ(result.length, (1U * 2U * 1U * 16U * 16U) / 2U); | ||
| 74 | + const auto elem_at = [&result](const size_t r, const size_t w) { | ||
| 75 | + const size_t idx = (w / 16U) * 256U + r * 16U + (w % 16U); | ||
| 76 | + return GetInt4Elem(result.data.get(), idx); | ||
| 77 | + }; | ||
| 78 | + EXPECT_EQ(elem_at(0, 0), 0U); | ||
| 79 | + EXPECT_EQ(elem_at(0, 1), 1U); | ||
| 80 | + EXPECT_EQ(elem_at(1, 17), (1U * 32U + 17U) % 16U); | ||
| 81 | + EXPECT_EQ(elem_at(3, 20), 0U); | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +// ND -> NZ -> ND round trip keeps data | ||
| 85 | +TEST_F(StestSubByteFormat, nd_to_nz_round_trip) { | ||
| 86 | + const std::vector<uint8_t> src = MakeInt4PackedData(2U * 32U); | ||
| 87 | + const Format nd_format = static_cast<Format>(GetFormatFromSubAndC0(FORMAT_ND, FORMAT_RESERVED, 5)); | ||
| 88 | + const Format nz_format = static_cast<Format>(GetFormatFromSubAndC0(FORMAT_FRACTAL_NZ, FORMAT_RESERVED, 5)); | ||
| 89 | + TransArgs to_nz{src.data(), nd_format, nz_format, FORMAT_ND, FORMAT_FRACTAL_NZ, FORMAT_RESERVED, | ||
| 90 | + FORMAT_RESERVED, 16, 16, {2, 32}, {1, 2, 1, 16, 16}, DT_INT4}; | ||
| 91 | + TransResult nz_result; | ||
| 92 | + FormatTransferFractalNz transfer; | ||
| 93 | + ASSERT_EQ(transfer.TransFormat(to_nz, nz_result), SUCCESS); | ||
| 94 | + TransArgs to_nd{nz_result.data.get(), nz_format, nd_format, FORMAT_FRACTAL_NZ, FORMAT_ND, FORMAT_RESERVED, | ||
| 95 | + FORMAT_RESERVED, 16, 16, {1, 2, 1, 16, 16}, {2, 32}, DT_INT4}; | ||
| 96 | + TransResult nd_result; | ||
| 97 | + FormatTransferFractalNzND reverse_transfer; | ||
| 98 | + ASSERT_EQ(reverse_transfer.TransFormat(to_nd, nd_result), SUCCESS); | ||
| 99 | + ASSERT_EQ(nd_result.length, src.size()); | ||
| 100 | + for (size_t i = 0U; i < src.size(); ++i) { | ||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include "formats/format_transfers/format_transfer_fractal_nz.h" | 14 | #include "formats/format_transfers/format_transfer_fractal_nz.h" |
| 15 | #include "formats/formats.h" | 15 | #include "formats/formats.h" |
| 16 | +#include "graph/types.h" | ||
| 16 | #include "time.h" | 17 | #include "time.h" |
| 17 | #include "framework/common/ge_inner_error_codes.h" | 18 | #include "framework/common/ge_inner_error_codes.h" |
| 18 | 19 | ||
| @@ -5002,5 +5002,13 @@ TEST_F(UtestFormatTranspose, null_src_transpose) { | |||
| 5002 | TransResult result; | 5002 | TransResult result; |
| 5003 | EXPECT_EQ(Transpose(nullptr, {2, 2}, DT_FLOAT16, {1, 0}, result), ACL_ERROR_GE_PARAM_INVALID); | 5003 | EXPECT_EQ(Transpose(nullptr, {2, 2}, DT_FLOAT16, {1, 0}, result), ACL_ERROR_GE_PARAM_INVALID); |
| 5004 | } | 5004 | } |
| 5005 | +// sub-byte dtype is rejected by transpose arg validation | ||
| 5006 | +TEST_F(UtestFormatTranspose, sub_byte_rejected) { | ||
| 5007 | + uint8_t data[4] = {1, 2, 3, 4}; | ||
| 5008 | + TransResult result; | ||
| 5009 | + EXPECT_EQ(TransposeWithShapeCheck(data, std::vector<int64_t>({2, 2}), std::vector<int64_t>({2, 2}), DT_INT4, | ||
| 5010 | + std::vector<int64_t>({1, 0}), result), | ||
| 5011 | + ACL_ERROR_GE_PARAM_INVALID); | ||
| 5012 | +} | ||
| 5005 | } // namespace formats | 5013 | } // namespace formats |
| 5006 | } // namespace ge | 5014 | } // namespace ge |
| @@ -340,6 +340,31 @@ NodePtr BuildHostCpuFoldNode(const std::string &op_type) { | |||
| 340 | return output; | 340 | return output; |
| 341 | } | 341 | } |
| 342 | 342 | ||
| 343 | +GeTensorPtr MakeHostCpuInputTensor(DataType data_type) { | ||
| 344 | + auto input_tensor = MakeShared<GeTensor>(); | ||
| 345 | + if (input_tensor == nullptr) { | ||
| 346 | + return nullptr; | ||
| 347 | + } | ||
| 348 | + input_tensor->MutableTensorDesc().SetShape(GeShape({8})); | ||
| 349 | + input_tensor->MutableTensorDesc().SetOriginShape(GeShape({8})); | ||
| 350 | + input_tensor->MutableTensorDesc().SetFormat(FORMAT_NCHW); | ||
| 351 | + input_tensor->MutableTensorDesc().SetOriginFormat(FORMAT_NCHW); | ||
| 352 | + input_tensor->MutableTensorDesc().SetDataType(data_type); | ||
| 353 | + input_tensor->MutableTensorDesc().SetOriginDataType(data_type); | ||
| 354 | + if (input_tensor->SetData(std::vector<uint8_t>{1, 2, 3, 4}) != SUCCESS) { | ||
| 355 | + return nullptr; | ||
| 356 | + } | ||
| 357 | + return input_tensor; | ||
| 358 | +} | ||
| 359 | + | ||
| 360 | +NodePtr BuildHostCpuFoldNode(const std::string &op_type, DataType output_dtype) { | ||
| 361 | + auto builder = ut::GraphBuilder("test"); | ||
| 362 | + auto input = builder.AddNode("input", CONSTANT, 0, 1, FORMAT_NCHW, DT_UINT8, {3}); | ||
| 363 | + auto output = builder.AddNode("output", op_type, 1, 1, FORMAT_NCHW, output_dtype, {3}); | ||
| 364 | + builder.AddDataEdge(input, 0, output, 0); | ||
| 365 | + return output; | ||
| 366 | +} | ||
| 367 | + | ||
| 343 | /** | 368 | /** |
| 344 | * netoutput1 | 369 | * netoutput1 |
| 345 | * | | 370 | * | |
| @@ -1536,4 +1561,65 @@ TEST_F(UtestGraphPassesConstantFoldingPass, test_compute_with_host_cpu_custom_op | |||
| 1536 | EXPECT_FALSE(node->GetOpDesc()->GetIrInputs().empty()); | 1561 | EXPECT_FALSE(node->GetOpDesc()->GetIrInputs().empty()); |
| 1537 | EXPECT_FALSE(node->GetOpDesc()->GetIrOutputs().empty()); | 1562 | EXPECT_FALSE(node->GetOpDesc()->GetIrOutputs().empty()); |
| 1538 | } | 1563 | } |
| 1564 | + | ||
| 1565 | +// ----------------------- sub-byte folding gate tests ----------------------- | ||
| 1566 | + | ||
| 1567 | +TEST_F(UtestGraphPassesConstantFoldingPass, sub_byte_input_blocked_for_non_whitelist_op) { | ||
| 1568 | + TestLegacyHostCpuOp::compute_count_ = 0; | ||
| 1569 | + const std::string op_type = "SubByteFoldBlocked"; | ||
| 1570 | + auto node = BuildHostCpuFoldNode(op_type); | ||
| 1571 | + ASSERT_NE(node, nullptr); | ||
| 1572 | + auto input_tensor = MakeHostCpuInputTensor(DT_INT4); | ||
| 1573 | + ASSERT_NE(input_tensor, nullptr); | ||
| 1574 | + std::vector<ConstGeTensorPtr> inputs{input_tensor}; | ||
| 1575 | + std::vector<GeTensorPtr> outputs; | ||
| 1576 | + | ||
| 1577 | + OpKernelRegistry::GetInstance().RegisterHostCpuOp(op_type, []() -> HostCpuOp * { return new TestLegacyHostCpuOp(); }); | ||
| 1578 | + EXPECT_EQ(ConstantFoldingPass::RunOpKernel(node, inputs, outputs), UNSUPPORTED); | ||
| 1579 | + EXPECT_EQ(TestLegacyHostCpuOp::compute_count_, 0); | ||
| 1580 | +} | ||
| 1581 | + | ||
| 1582 | +TEST_F(UtestGraphPassesConstantFoldingPass, sub_byte_input_whitelist_op_proceeds) { | ||
| 1583 | + TestLegacyHostCpuOp::compute_count_ = 0; | ||
| 1584 | + const std::string op_type = "QuantBiasOptimization"; | ||
| 1585 | + auto node = BuildHostCpuFoldNode(op_type); | ||
| 1586 | + ASSERT_NE(node, nullptr); | ||
| 1587 | + auto input_tensor = MakeHostCpuInputTensor(DT_INT4); | ||
| 1588 | + ASSERT_NE(input_tensor, nullptr); | ||
| 1589 | + std::vector<ConstGeTensorPtr> inputs{input_tensor}; | ||
| 1590 | + std::vector<GeTensorPtr> outputs; | ||
| 1591 | + | ||
| 1592 | + OpKernelRegistry::GetInstance().RegisterHostCpuOp(op_type, []() -> HostCpuOp * { return new TestLegacyHostCpuOp(); }); | ||
| 1593 | + EXPECT_EQ(ConstantFoldingPass::RunOpKernel(node, inputs, outputs), SUCCESS); | ||
| 1594 | + EXPECT_EQ(TestLegacyHostCpuOp::compute_count_, 1); | ||
| 1595 | +} | ||
| 1596 | + | ||
| 1597 | +TEST_F(UtestGraphPassesConstantFoldingPass, sub_byte_output_blocked_even_for_whitelist_op) { | ||
| 1598 | + TestLegacyHostCpuOp::compute_count_ = 0; | ||
| 1599 | + const std::string op_type = "QuantBiasOptimization"; | ||
| 1600 | + auto node = BuildHostCpuFoldNode(op_type, DT_INT4); | ||
| 1601 | + ASSERT_NE(node, nullptr); | ||
| 1602 | + auto input_tensor = MakeHostCpuInputTensor(); | ||
| 1603 | + ASSERT_NE(input_tensor, nullptr); | ||
| 1604 | + std::vector<ConstGeTensorPtr> inputs{input_tensor}; | ||
| 1605 | + std::vector<GeTensorPtr> outputs; | ||
| 1606 | + | ||
| 1607 | + OpKernelRegistry::GetInstance().RegisterHostCpuOp(op_type, []() -> HostCpuOp * { return new TestLegacyHostCpuOp(); }); | ||
| 1608 | + EXPECT_EQ(ConstantFoldingPass::RunOpKernel(node, inputs, outputs), UNSUPPORTED); | ||
| 1609 | + EXPECT_EQ(TestLegacyHostCpuOp::compute_count_, 0); | ||
| 1610 | +} | ||
| 1611 | + | ||
| 1612 | +TEST_F(UtestGraphPassesConstantFoldingPass, sub_byte_input_blocked_for_custom_op_path) { | ||
| 1613 | + TestHostCustomFoldOp::execute_count_ = 0; | ||
| 1614 | + auto node = BuildHostCpuFoldNode("IrDefinedHostFold"); | ||
| 1615 | + ASSERT_NE(node, nullptr); | ||
| 1616 | + auto input_tensor = MakeHostCpuInputTensor(DT_INT4); | ||
| 1617 | + ASSERT_NE(input_tensor, nullptr); | ||
| 1618 | + std::vector<ConstGeTensorPtr> inputs{input_tensor}; | ||
| 1619 | + std::vector<GeTensorPtr> outputs; | ||
| 1620 | + | ||
| 1621 | + ConstantFoldingPass pass; | ||
| 1622 | + EXPECT_EQ(pass.ComputeWithHostCpuCustomOp(node, inputs, outputs), UNSUPPORTED); | ||
| 1623 | + EXPECT_EQ(TestHostCustomFoldOp::execute_count_, 0); | ||
| 1624 | +} | ||
| 1539 | } // namespace ge | 1625 | } // namespace ge |
| @@ -108,3 +108,45 @@ TEST_F(UtestGraphPassesFoldingKernelkernelUtils, CheckSizeForTransOpSizeMismatch | |||
| 108 | bool ret = KernelUtils::CheckSizeForTransOp(tensor_0, op_desc_ptr); | 108 | bool ret = KernelUtils::CheckSizeForTransOp(tensor_0, op_desc_ptr); |
| 109 | EXPECT_EQ(false, ret); | 109 | EXPECT_EQ(false, ret); |
| 110 | } | 110 | } |
| 111 | + | ||
| 112 | +TEST_F(UtestGraphPassesFoldingKernelkernelUtils, CheckSizeForTransOpScalarFp16) { | ||
| 113 | + vector<uint8_t> data_vec_0 = {0x11, 0x22}; | ||
| 114 | + GeTensorDesc tensor_desc_0(GeShape(), FORMAT_NCHW, DT_FLOAT16); | ||
| 115 | + ConstGeTensorPtr tensor_0 = | ||
| 116 | + std::make_shared<GeTensor>(tensor_desc_0, (uint8_t *)data_vec_0.data(), data_vec_0.size()); | ||
| 117 | + | ||
| 118 | + OpDescPtr op_desc_ptr = std::make_shared<OpDesc>("TransOp", "TransOp"); | ||
| 119 | + GeTensorDesc input_desc(GeShape(), FORMAT_NCHW, DT_FLOAT16); | ||
| 120 | + op_desc_ptr->AddInputDesc(input_desc); | ||
| 121 | + | ||
| 122 | + bool ret = KernelUtils::CheckSizeForTransOp(tensor_0, op_desc_ptr); | ||
| 123 | + EXPECT_EQ(true, ret); | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +TEST_F(UtestGraphPassesFoldingKernelkernelUtils, CheckSizeForTransOpScalarSubByte) { | ||
| 127 | + vector<uint8_t> data_vec_0 = {0x5A}; | ||
| 128 | + GeTensorDesc tensor_desc_0(GeShape(), FORMAT_NCHW, DT_INT4); | ||
| 129 | + ConstGeTensorPtr tensor_0 = | ||
| 130 | + std::make_shared<GeTensor>(tensor_desc_0, (uint8_t *)data_vec_0.data(), data_vec_0.size()); | ||
| 131 | + | ||
| 132 | + OpDescPtr op_desc_ptr = std::make_shared<OpDesc>("TransOp", "TransOp"); | ||
| 133 | + GeTensorDesc input_desc(GeShape(), FORMAT_NCHW, DT_INT4); | ||
| 134 | + op_desc_ptr->AddInputDesc(input_desc); | ||
| 135 | + | ||
| 136 | + bool ret = KernelUtils::CheckSizeForTransOp(tensor_0, op_desc_ptr); | ||
| 137 | + EXPECT_EQ(true, ret); | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +TEST_F(UtestGraphPassesFoldingKernelkernelUtils, CheckSizeForTransOpScalarSizeMismatch) { | ||
| 141 | + vector<uint8_t> data_vec_0 = {0x11, 0x22, 0x33}; | ||
| 142 | + GeTensorDesc tensor_desc_0(GeShape(), FORMAT_NCHW, DT_FLOAT16); | ||
| 143 | + ConstGeTensorPtr tensor_0 = | ||
| 144 | + std::make_shared<GeTensor>(tensor_desc_0, (uint8_t *)data_vec_0.data(), data_vec_0.size()); | ||
| 145 | + | ||
| 146 | + OpDescPtr op_desc_ptr = std::make_shared<OpDesc>("TransOp", "TransOp"); | ||
| 147 | + GeTensorDesc input_desc(GeShape(), FORMAT_NCHW, DT_FLOAT16); | ||
| 148 | + op_desc_ptr->AddInputDesc(input_desc); | ||
| 149 | + | ||
| 150 | + bool ret = KernelUtils::CheckSizeForTransOp(tensor_0, op_desc_ptr); | ||
| 151 | + EXPECT_EQ(false, ret); | ||
| 152 | +} | ||
| @@ -536,3 +536,41 @@ TEST_F(UtestGraphPassesFoldingKernelPackKernel, ZeroInShapeSuccess) { | |||
| 536 | Status status = kernel->Compute(op_desc_ptr, input, outputs); | 536 | Status status = kernel->Compute(op_desc_ptr, input, outputs); |
| 537 | EXPECT_EQ(SUCCESS, status); | 537 | EXPECT_EQ(SUCCESS, status); |
| 538 | } | 538 | } |
| 539 | + | ||
| 540 | +// sub-byte dtype pack is skipped (fold ignored) to avoid byte-wise copy on packed data | ||
| 541 | +TEST_F(UtestGraphPassesFoldingKernelPackKernel, SubByteSkipped) { | ||
| 542 | + OpDescPtr op_desc_ptr = std::make_shared<OpDesc>("pack", "Pack"); | ||
| 543 | + vector<bool> is_input_const_vec = {true, true, true, true}; | ||
| 544 | + op_desc_ptr->SetIsInputConst(is_input_const_vec); | ||
| 545 | + AttrUtils::SetInt(op_desc_ptr, PACK_ATTR_NAME_NUM, (int64_t)4); | ||
| 546 | + AttrUtils::SetInt(op_desc_ptr, ATTR_NAME_T, (int64_t)DT_INT4); | ||
| 547 | + AttrUtils::SetInt(op_desc_ptr, ATTR_NAME_AXIS, (int64_t)0); | ||
| 548 | + op_desc_ptr->AddInputDesc(0, GeTensorDesc(GeShape({1}), FORMAT_NCHW, DT_INT4)); | ||
| 549 | + | ||
| 550 | + vector<int64_t> dims_vec_0; | ||
| 551 | + vector<uint8_t> data_vec_0 = {0}; | ||
| 552 | + GeTensorDesc tensor_desc_0(GeShape(dims_vec_0), FORMAT_NCHW, DT_INT4); | ||
| 553 | + ConstGeTensorPtr tensor_0 = std::make_shared<GeTensor>(tensor_desc_0, (uint8_t *)data_vec_0.data(), 1U); | ||
| 554 | + | ||
| 555 | + vector<int64_t> dims_vec_1; | ||
| 556 | + vector<uint8_t> data_vec_1 = {0}; | ||
| 557 | + GeTensorDesc tensor_desc_1(GeShape(dims_vec_1), FORMAT_NCHW, DT_INT4); | ||
| 558 | + ConstGeTensorPtr tensor_1 = std::make_shared<GeTensor>(tensor_desc_1, (uint8_t *)data_vec_1.data(), 1U); | ||
| 559 | + | ||
| 560 | + vector<int64_t> dims_vec_2; | ||
| 561 | + vector<uint8_t> data_vec_2 = {0}; | ||
| 562 | + GeTensorDesc tensor_desc_2(GeShape(dims_vec_2), FORMAT_NCHW, DT_INT4); | ||
| 563 | + ConstGeTensorPtr tensor_2 = std::make_shared<GeTensor>(tensor_desc_2, (uint8_t *)data_vec_2.data(), 1U); | ||
| 564 | + | ||
| 565 | + vector<int64_t> dims_vec_3; | ||
| 566 | + vector<uint8_t> data_vec_3 = {0}; | ||
| 567 | + GeTensorDesc tensor_desc_3(GeShape(dims_vec_3), FORMAT_NCHW, DT_INT4); | ||
| 568 | + ConstGeTensorPtr tensor_3 = std::make_shared<GeTensor>(tensor_desc_3, (uint8_t *)data_vec_3.data(), 1U); | ||
| 569 | + | ||
| 570 | + vector<ConstGeTensorPtr> input = {tensor_0, tensor_1, tensor_2, tensor_3}; | ||
| 571 | + vector<GeTensorPtr> outputs; | ||
| 572 | + | ||
| 573 | + shared_ptr<Kernel> kernel = KernelFactory::Instance().Create(PACK); | ||
| 574 | + auto status = kernel->Compute(op_desc_ptr, input, outputs); | ||
| 575 | + EXPECT_EQ(NOT_CHANGED, status); | ||
| 576 | +} | ||