| @@ -162,13 +162,19 @@ aclnnStatus aclnnIndex( | |||
| 162 | <td>传入的self是空指针。</td> | 162 | <td>传入的self是空指针。</td> |
| 163 | </tr> | 163 | </tr> |
| 164 | <tr> | 164 | <tr> |
| 165 | - <td rowspan="2">ACLNN_ERR_PARAM_INVALID</td> | 165 | + <td rowspan="4">ACLNN_ERR_PARAM_INVALID</td> |
| 166 | - <td rowspan="2">161002</td> | 166 | + <td rowspan="4">161002</td> |
| 167 | <td>self和out的数据类型不在支持的范围之内。</td> | 167 | <td>self和out的数据类型不在支持的范围之内。</td> |
| 168 | </tr> | 168 | </tr> |
| 169 | <tr> | 169 | <tr> |
| 170 | <td>self和out的数据类型不一致。</td> | 170 | <td>self和out的数据类型不一致。</td> |
| 171 | </tr> | 171 | </tr> |
| 172 | + <tr> | ||
| 173 | + <td>self为空且out非空。</td> | ||
C | |||
| 174 | + </tr> | ||
| 175 | + <tr> | ||
| 176 | + <td>indices中无tensor或所有tensor均为空时,out与self的shape不一致。</td> | ||
| 177 | + </tr> | ||
| 172 | </tbody> | 178 | </tbody> |
| 173 | </table> | 179 | </table> |
| 174 | 180 | ||
| @@ -220,6 +226,8 @@ aclnnStatus aclnnIndex( | |||
| 220 | - 确定性计算: | 226 | - 确定性计算: |
| 221 | - aclnnIndex默认确定性实现。 | 227 | - aclnnIndex默认确定性实现。 |
| 222 | - self非0维Tensor时,indices中tensor的个数需要小于等于self的维度数,self为0维Tensor时,indices中只能有1个tensor。 | 228 | - self非0维Tensor时,indices中tensor的个数需要小于等于self的维度数,self为0维Tensor时,indices中只能有1个tensor。 |
| 229 | +- self为空时,out必须为空,否则返回ACLNN_ERR_PARAM_INVALID。 | ||
| 230 | +- indices中无tensor或所有tensor均为空时,输出为self的副本,out的shape必须与self一致,否则返回ACLNN_ERR_PARAM_INVALID。 | ||
| 223 | - indices中的各个tensor中的值不能超过self中对应维度的大小,否则会产生不可预知行为,如地址越界。 | 231 | - indices中的各个tensor中的值不能超过self中对应维度的大小,否则会产生不可预知行为,如地址越界。 |
| 224 | <!-- npu="950" id9 --> | 232 | <!-- npu="950" id9 --> |
| 225 | - <term>Ascend 950PR&950DT系列产品</term>:indices各tensor之间shape需相同或满足broadcast关系。 | 233 | - <term>Ascend 950PR&950DT系列产品</term>:indices各tensor之间shape需相同或满足broadcast关系。 |
| @@ -200,6 +200,10 @@ static aclnnStatus CheckParams(const aclTensor* self, const aclTensorList* indic | |||
| 200 | // 4. 检查shape是否支持 | 200 | // 4. 检查shape是否支持 |
| 201 | CheckShape(self, indices); | 201 | CheckShape(self, indices); |
| 202 | CHECK_RET(CheckShapeDim(self, indices), ACLNN_ERR_PARAM_INVALID); | 202 | CHECK_RET(CheckShapeDim(self, indices), ACLNN_ERR_PARAM_INVALID); |
| 203 | + if (self->IsEmpty() && !out->IsEmpty()) { | ||
C 触发条件:self 非空(如 {30,10})、indices 仅含空 tensor(如 shape {0},按 ProcessIndicesAndMasks:408-421 的语义该维 mask=0)、out 非空(如 {30,10},即“该维不被索引、结果等于 self”的调用;IndexByTensor fallback 在 mask 全 0 时也会把被 mask 的维替换成空 tensor 后传入)。此时 indicesNum=0、allDefinedIndices 为空,CalculateOutputShapeAndTransposeFlag 仍返回 isAicore=true,函数在 aclnn_index.cpp:605 无条件调用 GetTransposedOutputShape(),其中 :339 直接取 allDefinedIndices[0]->GetViewShape(),对空 FVector 取下标属越界访问(UB,实际表现为进程崩溃),而不是返回 ACLNN_ERR_PARAM_INVALID。属存量缺陷(base 版本同样存在),本次新增的空 input 判据只按 self 判定,没有覆盖这一类空输入。修复建议:判据改为基于“推导出的输出是否为空 / out 是否与推导形状一致”,而不是只看 self->IsEmpty();或在 ProcessIndicesAndMasks 之后对 indicesNum==0 单独分支(全 mask 时直接 Contiguous+ViewCopy self→out,或明确返回 ACLNN_ERR_PARAM_INVALID),并禁止对空 allDefinedIndices 取 [0];同时补一条 indices 全空 + out 非空的 UT 作为回归保护。 如已修复请回复说明,我会在确认后把该意见标记为已解决。 ![]() ![]() | |||
| 204 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, "self is empty but out is non-empty."); | ||
| 205 | + return ACLNN_ERR_PARAM_INVALID; | ||
| 206 | + } | ||
| 203 | return ACLNN_SUCCESS; | 207 | return ACLNN_SUCCESS; |
| 204 | } | 208 | } |
| 205 | 209 | ||
| @@ -540,14 +544,35 @@ aclnnStatus aclnnIndexGetWorkspaceSize(const aclTensor* self, const aclTensorLis | |||
| 540 | auto ret = CheckParams(self, indices, out); | 544 | auto ret = CheckParams(self, indices, out); |
| 541 | CHECK_RET(ret == ACLNN_SUCCESS, ret); | 545 | CHECK_RET(ret == ACLNN_SUCCESS, ret); |
| 542 | 546 | ||
| 543 | - // 算子的空tensor在kernel中支持,对标竞品根据算子实际情况补充 | 547 | + bool allIndicesEmpty = true; |
| 544 | - bool selfOrOutIsEmpty = self->IsEmpty() || out->IsEmpty(); | 548 | + for (size_t i = 0; i < indices->Size(); i++) { |
| 545 | - if (selfOrOutIsEmpty) { | 549 | + if (!(*indices)[i]->IsEmpty()) { |
| 550 | + allIndicesEmpty = false; | ||
| 551 | + break; | ||
| 552 | + } | ||
| 553 | + } | ||
| 554 | + if (allIndicesEmpty) { | ||
| 555 | + OP_CHECK_SHAPE_NOT_EQUAL_WITH_EXPECTED_SIZE(out, self->GetViewShape(), return ACLNN_ERR_PARAM_INVALID); | ||
| 556 | + } | ||
| 557 | + | ||
| 558 | + // 空输出不需要生成计算图;空self与非空out已在参数检查阶段拒绝。 | ||
| 559 | + if (out->IsEmpty()) { | ||
| 546 | *workspaceSize = 0; | 560 | *workspaceSize = 0; |
| 547 | uniqueExecutor.ReleaseTo(executor); | 561 | uniqueExecutor.ReleaseTo(executor); |
| 548 | return ACLNN_SUCCESS; | 562 | return ACLNN_SUCCESS; |
| 549 | } | 563 | } |
| 550 | 564 | ||
| 565 | + // 空索引仅作为占位,不改变self;在进入索引检查和形状推导前处理该场景。 | ||
| 566 | + if (allIndicesEmpty) { | ||
| 567 | + auto selfContiguous = l0op::Contiguous(self, uniqueExecutor.get()); | ||
| 568 | + CHECK_RET(selfContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 569 | + auto viewCopyResult = l0op::ViewCopy(selfContiguous, out, uniqueExecutor.get()); | ||
| 570 | + CHECK_RET(viewCopyResult != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 571 | + *workspaceSize = uniqueExecutor->GetWorkspaceSize(); | ||
| 572 | + uniqueExecutor.ReleaseTo(executor); | ||
| 573 | + return ACLNN_SUCCESS; | ||
| 574 | + } | ||
| 575 | + | ||
| 551 | auto b4curArch = GetCurrentPlatformInfo().GetCurNpuArch(); | 576 | auto b4curArch = GetCurrentPlatformInfo().GetCurNpuArch(); |
| 552 | if (b4curArch == NpuArch::DAV_2201) { | 577 | if (b4curArch == NpuArch::DAV_2201) { |
| 553 | int64_t indicesSize = static_cast<int64_t>(indices->Size()); | 578 | int64_t indicesSize = static_cast<int64_t>(indices->Size()); |
| @@ -59,7 +59,7 @@ TEST_F(l2_index_test, l2_test_self_empty_fp32) | |||
| 59 | 59 | ||
| 60 | uint64_t workspaceSize = 0; | 60 | uint64_t workspaceSize = 0; |
| 61 | aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | 61 | aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); |
| 62 | - EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | 62 | + EXPECT_EQ(getWorkspaceResult, ACLNN_ERR_PARAM_INVALID); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | TEST_F(l2_index_test, l2_test_out_empty_fp32) | 65 | TEST_F(l2_index_test, l2_test_out_empty_fp32) |
| @@ -83,6 +83,81 @@ TEST_F(l2_index_test, l2_test_out_empty_fp32) | |||
| 83 | EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | 83 | EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | +TEST_F(l2_index_test, l2_test_self_and_out_empty_fp32) | ||
| 87 | +{ | ||
| 88 | + vector<int64_t> self_shape = {0, 10}; | ||
| 89 | + vector<int64_t> indices_shape = {0}; | ||
| 90 | + vector<int64_t> out_shape = {{0, 10}}; | ||
| 91 | + | ||
| 92 | + auto self_desc = TensorDesc(self_shape, ACL_FLOAT, ACL_FORMAT_ND).ValueRange(1, 4); | ||
| 93 | + auto indices_desc = TensorDesc(indices_shape, ACL_INT64, ACL_FORMAT_ND).ValueRange(1, 25); | ||
| 94 | + auto tensor_list_desc = TensorListDesc({ | ||
| 95 | + indices_desc, | ||
| 96 | + }); | ||
| 97 | + auto out_desc = TensorDesc(out_shape, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 98 | + | ||
| 99 | + auto ut = OP_API_UT(aclnnIndex, INPUT(self_desc, tensor_list_desc), // host api输入 | ||
| 100 | + OUTPUT(out_desc)); | ||
| 101 | + | ||
| 102 | + uint64_t workspaceSize = 0; | ||
| 103 | + aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | ||
| 104 | + EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | ||
| 105 | + EXPECT_EQ(workspaceSize, 0); | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +TEST_F(l2_index_test, l2_test_all_indices_empty_fp32) | ||
| 109 | +{ | ||
| 110 | + auto self_desc = TensorDesc({30, 10}, ACL_FLOAT, ACL_FORMAT_ND).ValueRange(1, 4); | ||
| 111 | + auto indices_desc = TensorDesc({0}, ACL_INT64, ACL_FORMAT_ND); | ||
| 112 | + auto out_desc = TensorDesc({30, 10}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 113 | + | ||
| 114 | + for (int indices_count = 1; indices_count <= 2; ++indices_count) { | ||
| 115 | + SCOPED_TRACE(indices_count); | ||
| 116 | + auto tensor_list_desc = TensorListDesc(indices_count, indices_desc); | ||
| 117 | + auto ut = OP_API_UT(aclnnIndex, INPUT(self_desc, tensor_list_desc), OUTPUT(out_desc)); | ||
| 118 | + uint64_t workspaceSize = 0; | ||
| 119 | + EXPECT_EQ(ut.TestGetWorkspaceSize(&workspaceSize), ACLNN_SUCCESS); | ||
| 120 | + } | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +TEST_F(l2_index_test, l2_test_all_indices_empty_non_contiguous_fp32) | ||
| 124 | +{ | ||
| 125 | + auto self_desc = TensorDesc({30, 10}, ACL_FLOAT, ACL_FORMAT_ND, {1, 30}, 0, {10, 30}).ValueRange(1, 4); | ||
| 126 | + auto indices_desc = TensorDesc({0}, ACL_INT64, ACL_FORMAT_ND); | ||
| 127 | + auto tensor_list_desc = TensorListDesc({indices_desc, indices_desc}); | ||
| 128 | + auto out_desc = TensorDesc({30, 10}, ACL_FLOAT, ACL_FORMAT_ND, {1, 30}, 0, {10, 30}); | ||
| 129 | + | ||
| 130 | + auto ut = OP_API_UT(aclnnIndex, INPUT(self_desc, tensor_list_desc), OUTPUT(out_desc)); | ||
| 131 | + uint64_t workspaceSize = 0; | ||
| 132 | + EXPECT_EQ(ut.TestGetWorkspaceSize(&workspaceSize), ACLNN_SUCCESS); | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +TEST_F(l2_index_test, l2_test_all_indices_empty_scalar_fp32) | ||
| 136 | +{ | ||
| 137 | + auto self_desc = TensorDesc({}, ACL_FLOAT, ACL_FORMAT_ND).ValueRange(1, 4); | ||
| 138 | + auto indices_desc = TensorDesc({0}, ACL_INT64, ACL_FORMAT_ND); | ||
| 139 | + auto tensor_list_desc = TensorListDesc({indices_desc}); | ||
| 140 | + auto out_desc = TensorDesc({}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 141 | + | ||
| 142 | + auto ut = OP_API_UT(aclnnIndex, INPUT(self_desc, tensor_list_desc), OUTPUT(out_desc)); | ||
| 143 | + uint64_t workspaceSize = 0; | ||
| 144 | + EXPECT_EQ(ut.TestGetWorkspaceSize(&workspaceSize), ACLNN_SUCCESS); | ||
| 145 | +} | ||
| 146 | + | ||
| 147 | +TEST_F(l2_index_test, l2_test_all_indices_empty_invalid_out_shape_fp32) | ||
| 148 | +{ | ||
| 149 | + auto self_desc = TensorDesc({30, 10}, ACL_FLOAT, ACL_FORMAT_ND).ValueRange(1, 4); | ||
| 150 | + auto indices_desc = TensorDesc({0}, ACL_INT64, ACL_FORMAT_ND); | ||
| 151 | + auto tensor_list_desc = TensorListDesc({indices_desc}); | ||
| 152 | + | ||
| 153 | + for (const auto& out_shape : vector<vector<int64_t>>{{10, 30}, {30}, {0, 10}}) { | ||
| 154 | + auto out_desc = TensorDesc(out_shape, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 155 | + auto ut = OP_API_UT(aclnnIndex, INPUT(self_desc, tensor_list_desc), OUTPUT(out_desc)); | ||
| 156 | + uint64_t workspaceSize = 0; | ||
| 157 | + EXPECT_EQ(ut.TestGetWorkspaceSize(&workspaceSize), ACLNN_ERR_PARAM_INVALID); | ||
| 158 | + } | ||
| 159 | +} | ||
| 160 | + | ||
| 86 | TEST_F(l2_index_test, l2_test_success_multi_indices_aicore) | 161 | TEST_F(l2_index_test, l2_test_success_multi_indices_aicore) |
| 87 | { | 162 | { |
| 88 | vector<int64_t> self_shape = {30, 30}; | 163 | vector<int64_t> self_shape = {30, 30}; |
| @@ -432,4 +507,4 @@ TEST_F(l2_index_test, ascend950_l2_test_success_fp32) | |||
| 432 | uint64_t workspaceSize = 0; | 507 | uint64_t workspaceSize = 0; |
| 433 | aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); | 508 | aclnnStatus getWorkspaceResult = ut.TestGetWorkspaceSize(&workspaceSize); |
| 434 | EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); | 509 | EXPECT_EQ(getWorkspaceResult, ACLNN_SUCCESS); |
| 435 | -} | 510 | +} |


本次只在返回值错误码表新增“self为空且out非空”,而同一文档的“约束说明”(aclnnIndex.md:221-233)仍只有 indices 个数、indices 取值不越界、bool 索引与 broadcast 三类约束,没有写“self 为空时 out 必须为空,否则返回 ACLNN_ERR_PARAM_INVALID”。后果:使用者按约束章节自检时仍会认为 self 空 + out 非空是可用组合,直到调用时才拿到 161002,接口文档两处口径不一致。修复建议:在约束说明补一条同义约束,并与错误码表措辞保持一致。
如已修复请回复说明,我会在确认后把该意见标记为已解决。