您好,感谢反馈!按提交 584b2ea6 核对,PowScalarTensor 与非原地 Subs 都没有独立检查 out 是否位于各自的公开类型列表内,仅靠转换关系检查,确有让不支持的输出类型进入后续构图的缺口。报告的错误码差异可由两条不同的返回链解释。
源码核对
PowScalarTensor:共享类型检查只检查两个输入的类型列表;计算分支中末尾 Cast返回空指针时,辅助函数先返回561103,而入口将辅助函数失败统一映射为561000。这里应定位到 ScalarTensor 的辅助函数,报告引用的 485 行属于另一个 TensorScalar 入口。Subs:实际调用的是 CheckParamsScalar,其中只对白名单检查self,并未独立检查out;输出 Cast 失败则直接映射为561103。报告引用的 240 行是 Tensor 版本的检查函数。InplaceSubs:转发时 selfRef 同时作为输入和输出。因此,selfRef=INT4会先被输入白名单检查拒绝为161002;现有复现脚本也没有调用该原地入口,尚不能将相同的“独立 out 非法”路径和561103结论直接推广到它。
处理方向
建议在两个独立输出入口的参数阶段,按产品与具体接口补齐输出类型范围,再保留已有推导、alpha 和转换关系校验,使不支持的类型尽早返回 161002。内部计算对象失败仍应保留内部错误码。
输出列表须按入口分别核对:PowScalarTensor输出比输入多 UINT16/UINT32/UINT64,且与 TensorScalar 的 BOOL 描述不同,不能把同一个输入列表直接复用到共享检查;Subs 的输出列表实际包含 BOOL,所以该类型的转换失败不能解释为“BOOL 不在输出列表”。已有通用类型检查宏可以承载输出检查,新宏属于可选的代码组织方式;也不能仅凭全仓是否出现某个宏就判定其他接口都存在同样缺陷。
本次未新增硬件复测,外部 CanCast 对全部扫描类型的具体判定也尚未独立验证。后续将继续按入口核对输出类型、返回码与原地别名边界,跟踪合法类型及转换规则的回归确认。


Subs 部分已提交修复:!6792
CheckPromoteType增加 out 白名单(含平台相关的 BF16 处理),越界 dtype 第一段返回 161002;aclnnInplaceSubs经 selfRef 复用同一路径。
真机(910B / CANN 8.5.1):修复前 out=UINT16 返回 0 且执行成功,修复后返回 161002;out=FLOAT 仍为 0。
PowScalarTensor 未包含在本 MR 中(8.5.1 无该接口头文件,未做真机验证)。


更正:Subs 修复的 MR 号是 !6793(上一条中 "!6792" 为笔误)。


补充:PowScalarTensor 也已被 !6796 覆盖(与 PowTensorScalar 共用 CheckDtypeValid 的 out 白名单;真机以 PowTensorScalar 的 INT4 → 161002 验证)。Subs 部分见 !6793。


/assign @doufloat


已按兼容性要求将修正范围收敛到 A2/A3:
- A2/A3 均走
DAV_2201路径。新增 out dtype 白名单仅在DAV_2201启用,非法 out 在 ACLNN 第一段参数校验中返回ACLNN_ERR_PARAM_INVALID(161002)。 - A5 走
DAV_3510路径,不启用新增白名单,保持原有CanCast及后续 Build 行为不变。 - A2/A3 合法 out dtype 路径不变;本次只修正非法 dtype 的错误码语义。
- A5 真机实测保持基线行为:
PowScalarTensor的out=INT4/UINT1/COMPLEX32返回561000,PowTensorScalar/PowTensorTensor/Subs/Sub的对应场景返回561103;HIFLOAT8/FLOAT8_E5M2仍返回0。 - A2/A5 均使用当前补丁运行
math_op_api_ut,结果均为158/158 PASS。A2 真机实测非法 out 返回161002,合法INT64 -> UINT16/UINT32/UINT64仍返回0。 - A3 与 A2 使用相同的
DAV_2201实现路径;本轮 A2 已完成真机验证,A3 未单独上板复测。
兼容性影响:本次只改变 A2/A3 非法 out dtype 的报错语义,不改变合法 dtype 行为;A5 暂不修正,保持现有行为。
关联 PR:https://gitcode.com/cann/ops-math/pull/6800
PR head:d803c07af069cf7444e95d5a1ea535a8d1246dfe


aclnnPowScalarTensor/aclnnSubs/aclnnInplaceSubs在out的 dtype 传资料输出支持列表之外的值(如int4)时,第一段接口返回内部错误码,而资料错误码表要求161002(ACLNN_ERR_PARAM_INVALID)。aclnnPowScalarTensor:out=DT_INT4→561000(ACLNN_ERR_INNER)aclnnSubs/aclnnInplaceSubs:out=DT_INT4→561103(ACLNN_ERR_INNER_NULLPTR)非法入参确实被拒了,但错误码降级为「内部错误 / 内部空指针」,调用方无法按资料区分「参数非法」与「算子内部失败」。
二、环境信息 (必填)
/usr/local/Ascend/ascend-toolkit/latest)cann/ops-math584b2ea6cbaeba684a51a74296d82d4d50ab3b0f(origin/master,2026-10-24)三、复现步骤 (必填)
repro.cppexport ASCEND_HOME_PATH=/usr/local/Ascend/ascend-toolkit/latestg++ -std=c++17 -O2 repro.cpp -o repro -I$ASCEND_HOME_PATH/include -L$ASCEND_HOME_PATH/lib64 -lnnopbase -lascendcl -lopapi -lpthread./repro#include <cstdio> #include <cstdint> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_pow.h" #include "aclnnop/aclnn_sub.h" static aclTensor* mk(const std::vector<int64_t>& sh, aclDataType dt, void* dev) { std::vector<int64_t> st(sh.size(), 1); for (int64_t i = (int64_t)sh.size() - 2; i >= 0; --i) { st[i] = sh[i + 1] * st[i + 1]; } return aclCreateTensor(sh.data(), sh.size(), dt, st.data(), 0, ACL_FORMAT_ND, sh.data(), sh.size(), dev); } int main() { aclInit(nullptr); aclrtSetDevice(0); const int64_t N = 32; for (auto pr : {std::make_pair(ACL_FLOAT, "FLOAT(ctrl)"), std::make_pair(ACL_BOOL, "BOOL(ctrl)"), std::make_pair(ACL_INT4, "INT4(defect)")}) { void* ed = nullptr; aclrtMalloc(&ed, N * 4, ACL_MEM_MALLOC_HUGE_FIRST); aclTensor* e = mk({4, 8}, ACL_FLOAT, ed); void* od = nullptr; aclrtMalloc(&od, N + 128, ACL_MEM_MALLOC_HUGE_FIRST); aclTensor* o = mk({4, 8}, pr.first, od); float sv = 2.0f; aclScalar* s = aclCreateScalar(&sv, ACL_FLOAT); uint64_t ws = 0; aclOpExecutor* ex = nullptr; int ret = aclnnPowScalarTensorGetWorkspaceSize(s, e, o, &ws, &ex); printf("PowScalarTensor out=%-14s ret=%d ", pr.second, ret); aclDestroyScalar(s); aclDestroyTensor(e); aclDestroyTensor(o); aclrtFree(ed); aclrtFree(od); } for (auto pr : {std::make_pair(ACL_FLOAT, "FLOAT(ctrl)"), std::make_pair(ACL_BOOL, "BOOL(ctrl)"), std::make_pair(ACL_INT4, "INT4(defect)")}) { void* sd = nullptr; aclrtMalloc(&sd, N * 4, ACL_MEM_MALLOC_HUGE_FIRST); aclTensor* s = mk({4, 8}, ACL_FLOAT, sd); void* od = nullptr; aclrtMalloc(&od, N + 128, ACL_MEM_MALLOC_HUGE_FIRST); aclTensor* o = mk({4, 8}, pr.first, od); float ov = 2.0f, av = 1.2f; aclScalar* other = aclCreateScalar(&ov, ACL_FLOAT); aclScalar* alpha = aclCreateScalar(&av, ACL_FLOAT); uint64_t ws = 0; aclOpExecutor* ex = nullptr; int ret = aclnnSubsGetWorkspaceSize(s, other, alpha, o, &ws, &ex); printf("Subs out=%-14s ret=%d ", pr.second, ret); aclDestroyScalar(other); aclDestroyScalar(alpha); aclDestroyTensor(s); aclDestroyTensor(o); aclrtFree(sd); aclrtFree(od); } aclrtResetDevice(0); aclFinalize(); return 0; }四、实际输出
补充扫描(同一脚本,
out全 dtype):规律:「在列表内但不兼容」与「CanCast 能识别的不兼容」都能正确返回 161002;只有「完全不在列表且 CanCast 放行」的 dtype 才漏到内部错误码。
五、首条 plog(定位漏在哪一层)
ASCEND_GLOBAL_LOG_LEVEL=1后aclnnPowScalarTensor out=INT4:首条落在
Cast,不在CheckParams—— 直接指认「参数校验阶段没拦住」。六、根因(源码锚点)
math/pow/op_api/aclnn_pow.cpp:164 CheckDtypeValid:if (!CheckType(selfDtype, DTYPE_SUPPORT_LIST)) { ...; return false; } // L170 if (!CheckType(expDtype, DTYPE_SUPPORT_LIST)) { ...; return false; } // L175 op::DataType promoteType = op::PromoteType(selfDtype, expDtype); if (promoteType == DataType::DT_UNDEFINED) { ...; return false; } OP_CHECK_RESULT_DTYPE_CAST_FAILED(promoteType, outDtype, return false); // L187 只有 CanCastCanCast(fp32, DT_INT4)返回 true → 放行 → 进入BuildPowScalarTensorCompute→ L485l0op::Cast(powOut, out->GetDataType(), ...)返回空 → L486CHECK_RET(..., ACLNN_ERR_INNER_NULLPTR)→ 561000。
math/sub/op_api/aclnn_sub.cpp:240 CheckParams:OP_CHECK_DTYPE_NOT_SUPPORT(self, dtypeSupportList, return ACLNN_ERR_PARAM_INVALID); // L248 OP_CHECK_DTYPE_NOT_SUPPORT(other, dtypeSupportList, return ACLNN_ERR_PARAM_INVALID); // L249 // ← 缺 outaclnnInplaceSubs与aclnnSubs共用同一CheckParams,因此同源。七、同仓正面实现(修复参照)
grep -rn 'OP_CHECK_DTYPE_NOT_SUPPORT' \ math/eye/op_api/aclnn_eye.cpp math/reduce_all/op_api/aclnn_all.cpp \ math/sub/op_api/aclnn_sub.cpp math/pow/op_api/aclnn_pow.cppmath/eye/op_api/aclnn_eye.cpp:60OP_CHECK_DTYPE_NOT_SUPPORT(out, dtypeSupportList, return false)math/reduce_all/op_api/aclnn_all.cpp:157OP_CHECK_DTYPE_NOT_SUPPORT(out, out_dtype_support_list, return false)math/sub/op_api/aclnn_sub.cpp:248-249math/pow/op_api/aclnn_pow.cpp全仓扫描(
op_api/aclnn_*.cpp,546 个接口):有 out 白名单 230 个、缺 316 个。建议在公共宏层(
op_error_check.h)加一条OP_CHECK_OUT_DTYPE_NOT_SUPPORT,把「是否给 out 建白名单」变成显式选择,而不是默认漏掉。
八、期望行为
补上与
aclnnEye/aclnnAll一致的 out 白名单检查,使上述场景返回161002:// pow:OUT_DTYPE_SUPPORT_LIST 取资料 out 列表(比 self/exponent 多 UINT16/UINT32/UINT64) if (!CheckType(outDtype, OUT_DTYPE_SUPPORT_LIST)) { OP_LOGE(ACLNN_ERR_PARAM_INVALID, "out dtype %s should be in dtype support list %s.", op::ToString(outDtype).GetString(), op::ToString(OUT_DTYPE_SUPPORT_LIST).GetString()); return false; } // sub: OP_CHECK_DTYPE_NOT_SUPPORT(out, dtypeSupportList, return ACLNN_ERR_PARAM_INVALID);九、影响面
561000= 内部错误、561103= 内部空指针。上层框架按161002分支做参数纠错提示,收到 56xxxx 会走「算子内部异常」路径 → 误判为算子 bug,
掩盖真实原因(调用方传错 out dtype),并拉长定位链路。
十、附:同批确认「不是缺陷」的相邻项(供维护者区分)
out=UINT16/UINT32/UINT64(在列表但 fp32 推不进去)self=fp32 + exponent=int32 + out=int32self=2(int) + exponent=int32 + out=int32(同型)subs: self=int32 + alpha=fp32(1.2)subs: self=int32 + other=int32 + alpha=int32(同型)exponentrank=9 / shape 不一致pow: self=int64 + exponent=bool + out=boolCanCast(int64,bool)=false,符合资料pow: self=bool + exponent=bool