Merge blocked
变更摘要
本 PR 为 Challenge13 的 exercises 算子提交,围绕 PyTorch 原生 torch.nn.functional.leaky_relu(y = max(x,0) + negative_slope * min(x,0))实现了一套完整的 AscendC 自定义算子工程:包含算子原型定义、形状推导、host 侧 tiling、kernel 侧计算实现,以及配套的构建脚本、aclnn 示例、样本生成/校验脚本和 UT 测试框架。核心设计是将 negative_slope 的 IEEE-754 位模式编码进 tiling 数据传给 kernel,并按 dtype 区分 float16/float32 两条计算路径。
主要改动
- 算子原型与形状推导:新增
op_host/leakyrelu_def.cpp,注册Leakyrelu的输入x、输出y(DT_FLOAT/DT_FLOAT16、FORMAT_ND、AutoContiguous)、Float 属性negative_slope及ascend910bAICore 配置;op_host/leakyrelu_infershape.cpp通过IMPL_OP_INFERSHAPE将输入 shape 直接透传到输出。 - Tiling 策略与参数编码:
op_host/leakyrelu_tiling.cpp中LeakyreluTilingFunc读取negative_slope属性并在缺失/非法(位模式判断非有限数)时回退默认0.01;把 slope 位模式编码到ubFactor高 32 位,并约定位模式 0 为“未设置”哨兵、slope == 0编码为 denormal 位 1;同时按MIN_SPLIT_THRESHOLD与UB_BYTES_PER_ELEM(16 字节/元素)计算blockDim、blockFactor、UB 分块元素数,并按输入 dtype 分别设置LEAKYRELU_TPL_SCH_MODE_0/1的 tilingKey。 - Kernel 计算实现:
op_kernel/leakyrelu.h的NsLeakyrelu::Leakyrelu<T>实现Init/CopyIn/Compute/CopyOut/Process,使用双缓冲TQue与DataCopyPad处理非对齐尾块;half 输入走Cast → LeakyRelu(float32) → Cast的 float32 中间路径,float 输入直接原地计算;op_kernel/leakyrelu.cpp按schMode模板参数实例化half/float两个 kernel。 - 样本与示例链路:新增
samples/case.json(x/y均为8x2048float32、negative_slope=0.01、rtol/atol=1e-4)、samples/reference.py(float64 中间计算后回写原 dtype)与samples/sample.py(generate/verify生成与比对 golden);examples/test_aclnn_leakyrelu.cpp调用aclnnLeakyreluGetWorkspaceSize/aclnnLeakyrelu完成端到端验证。 - 构建与 UT 框架:
build.sh提供-j、--make_clean、-u/--ut、-e/--example选项并基于源码指纹做增量构建;tests/ut下新增tiling_context_faker、infershape_context_faker、tiling_case_executor/infershape_case_executor及AnyValue等测试基础设施,并配套test_leakyrelu_tiling.cpp(tiling 用例断言)与基于tikicpulib的test_leakyrelu.cppkernel UT。


/compile


代码审查
审查结论
- [建议]
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/DingLabChina_JeffDing/code/exercises/build.sh:38fix: 未加引号的 rm -rf ${BUILD_PATH}/* 在路径含空格时会删除错误目录 - [建议]
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/DingLabChina_JeffDing/code/exercises/op_kernel/leakyrelu.h:80fix: negative_slope 位模式为 0 时被误判为"未设置"并回退 0.01 - [建议]
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/DingLabChina_JeffDing/code/exercises/op_kernel/leakyrelu.h:64fix: 不要用 int64 ubFactor 的高低 32 位混装元素数与 slope 位模式 - [阻塞]
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/DingLabChina_JeffDing/code/exercises/tests/ut/op_host/test_leakyrelu_tiling.cpp:36fix: op_host UT 的期望 tilingData 是模板占位值,必然断言失败 - [建议]
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/DingLabChina_JeffDing/code/exercises/tests/ut/op_kernel/test_leakyrelu.cpp:66fix: kernel UT 未校验 GmAlloc 返回值即写入/读取 - [建议]
2026/CANN-Code-Detective/Challenge13-LeakyReluCustom/PR_code/DingLabChina_JeffDing/code/exercises/tests/ut/run.sh:5fix: ENABLE_INNER_KERNEL_UT 默认 OFF 却无条件执行 kernel UT
| 类型 | 数量 |
|---|---|
| 🔴 阻塞 | 1 |
| 🟡 建议 | 5 |
⛔ 需要修改


Thanks for your pull-request.
The full list of commands accepted by me can be found at here.
You can get sig-info at here.
You can self-configure the PR merge rules for this repository. For more details, please refer to Here.
For more, you also can visit HICANN.
PR Approval Progress
⚠️ This PR does not yet meet the following requirements:lgtm (requires ≥ 2 person(s) per module)、approve (requires ≥ 1 person(s) per module)
Module Approval Details
| module | lgtm status | approve status |
|---|---|---|
| repo-cann/cann-outreach | ❌ (0/2)(You can also ask: 田晓亮, 颜亚文, Carolina_yuan, shaoyf, 王艳秋) | ❌ (0/1)(You can also ask: 傅涛, luzx66, 王艳秋, gcw_tgPdOLZT, jxlang) |
💡 Tip:
- Committer can comment
/approveor/lgtm- Commenting
/approveimplies both code review (lgtm) and intent to merge (approve)
CLA Signature Pass
JeffDing, thanks for your pull request. All authors of the commits have signed the CLA. 👍


🟡 Medium Priority
变更行 tests/ut/op_kernel/test_leakyrelu.cpp:66-69 连续四次 AscendC::GmAlloc(...),返回值没有任何非空判断,随后第 71 行立刻 f.read(reinterpret_cast<char*>(x), 65536) 把文件内容写进 x,第 77 行把 tiling 指针 reinterpret_cast 后解引用赋值,第 92 行 memcpy 从 y 读出。
失败模式:GmAlloc 在 CPU 模拟器/受限内存下可能返回 nullptr,此时第 71 行向 nullptr 写入 65536 字节、或第 78-80 行向 nullptr 写入字段,直接段错误(测试崩溃且无任何提示信息),而不是给出可诊断的断言失败。工作区中同文件第 64 行已经对 getenv 做了断言检查,说明作者本意是防御性写法,这里遗漏了。
修复:每个 GmAlloc 之后 ASSERT_NE(ptr, nullptr),或改用 std::unique_ptr + 自定义 deleter 托管。
建议:在四次 GmAlloc 之后分别加 ASSERT_NE(x/y/workspace/tiling, nullptr),或封装成 RAII 句柄,保证分配失败时给出明确断言而非段错误。


🟡 Medium Priority
变更行:build.sh:38 rm -rf ${BUILD_PATH}/*(同类问题见 :45 rm -rf ${BUILD_OUT_PATH}/*,以及 :5 未加引号的 dirname $0)。
问题链条:BUILD_PATH 由 BASE_PATH(即 $(cd "$(dirname $0)" && pwd) 的结果)拼接而成,脚本对该路径没有任何字符约束。该行 ${BUILD_PATH} 未加引号,处于命令位置的操作数会被分词并做 glob 展开。
失效模式:当脚本所在目录包含空格(例如 /home/user/my code/exercises)时,${BUILD_PATH}/* 展开为多个词:/home/user/my、code/exercises/build/*。rm -rf 于是递归删除 /home/user/my(上一个目录层级中的真实目录),并把相对路径 code/exercises/build/* 当作当前工作目录下的路径处理——前一个 [ -d "${BUILD_PATH}" ] 守卫因为是加引号的、能通过,无法拦住此破坏。--make_clean 是文档中公开的入口,触发条件明确、后果是不可恢复的误删。第 45 行同理。
修复方向:所有拼接路径的 rm 操作数都必须加引号(并可考虑用 ${BUILD_PATH:?} 防止空值),与文件内 :133 rm -rf "$BUILD_PATH" 的写法保持一致;:5 的 dirname "$0" 也应加引号。
建议:给 rm 的目标路径加引号(必要时用 ${VAR:?} 防空值);第 45 行同样处理,第 5 行改为 dirname "$0"。
| 38
| - rm -rf ${BUILD_PATH}/* |
|
38 | + rm -rf "${BUILD_PATH:?}/"* |


🟡 Medium Priority
改动行:leakyrelu.h:80-86(配合 22 行 LEAKY_RELU_SLOPE_ZERO_CODE = 0U)。
行为/契约:代码用"slope 的 IEEE-754 位模式是否为 0"来区分"属性未设置"和"已设置",位模式为 0 时把 slope_ 覆盖成默认值 0.01f。
失效模式:0.0f 的 IEEE-754 位模式恰好就是 0x00000000,因此 negative_slope = 0 这个 PyTorch 合法取值(等价于 ReLU,负半轴全部输出 0)会被静默替换为 0.01。触发条件:用例 attributes 中 negative_slope 取 0(框架默认值与 case.json 都是 0.01,所以现有用例发现不了)。此时所有负输入会输出 0.01*x 而不是 0,出现确定性 Wrong Answer。这说明"未设置"与"值为 0"语义被混用,不能靠数值本身当哨兵。
修复方向:用一个显式的存在性标记(例如 tiling 结构体中独立的 float negativeSlope 字段,或单独的 bool hasSlope/slopeBits 哨兵取 NaN 位模式等不可能取值),确保 slopeCode==0 时按 0.0f 正常参与计算。
建议:不要用 slope 的位模式 0 表达"未设置"。在 TilingData 中新增独立字段承载 negative_slope(见另一条 finding),kernel 直接读取该字段赋值给 slope_;或使用专门的哨兵(如 0xFFFFFFFF/NaN 位模式和显式 hasSlope 标记),使 negative_slope=0 能按 0.0f 正常生效。


🟡 Medium Priority
改动行:leakyrelu.h:64-65 把 tilingData->ubFactor 拆成低 32 位(UB 循环元素数)与高 32 位(negative_slope 的 IEEE-754 位模式);同时新增的 leakyrelu_tiling_data.h:12 把同一字段声明并注释为"每次 UB 循环处理的元素数量"。
行为/契约:同一字段在两个文件里语义不一致——结构体声明它是纯元素数,kernel 却把它当作 64 位打包值。任何一个按结构体注释/声明生成 tiling 的生产者(host tiling、UT tiling case、按字段名取值的断言)写入纯元素数时,高 32 位为 0,kernel 侧 slopeCode 恒为 0,所有非 0.01 的 negative_slope 用例都会静默按 0.01 计算;反之若生产者真的打包,ubFactor 会变成约 4e18 的异常大值,任何把它当元素数使用/校验的宿主侧代码都会失配。
失效模式:属性取值语义漂移导致的 Wrong Answer,且当前 case.json 只用默认 0.01,无法暴露该不一致。packing 约定跨文件且不可由 kernel 单侧自洽(违反算子规范"TilingID/TilingKey 字段语义需明确"的精神,正确做法是追加显式字段而非复用位)。
修复方向:在 LeakyreluTilingData 末尾追加独立字段(如 float negativeSlope 或 int32_t slopeBits + bool hasSlope),ubFactor 恢复为纯元素数;Init 中直接按字段解包,去掉移位掩码。
建议:在 LeakyreluTilingData 末尾追加独立的 negative_slope 字段(float 或 int32 位模式 + 显式存在性标记),并让 Init 直接读取该字段;ubFactor 只表达 UB 循环元素数,不再承载 slope 位模式。需同步确认 host tiling 的写入方式(本 PR 未包含该文件,无法在此验证)。


🟠 High Priority
变更行(tests/ut/op_host/test_leakyrelu_tiling.cpp:36)里 case "leakyrelu_0" 期望 expectTilingData = "0 1 0 "、expectWorkspaces = {0}、expectTilingKey = 1。
证据链:
- 该字符串正是
LeakyreluTilingData的成员默认值(op_kernel/leakyrelu_tiling_data.h:10-12:totalNum=0、blockFactor=1、ubFactor=0),而不是实际 tiling 输出。 - op_host/leakyrelu_tiling.cpp 对 shape {8,2048} float 的实际计算为:totalNum=16384(第 115 行 GetShapeSize),blockDim=min(64, CeilDiv(16384,1024))=16(第 125-131 行),blockFactor=CeilDiv(16384,16)=1024(第 132 行),ubFactor=ubElems | (slopeCode<<32),其中 slopeCode 是 0.01f 的位模式 0x3C23D70A(第 151-155 行)。
- common/tiling_case_executor.cpp:275-278 会用
to_string<int64_t>(rawTilingData->GetData(), GetDataSize())与期望串做 EXPECT_EQ。
失败模式:三个 64 位字段的实际值(16384、1024、0x3C23D70A0000xxxx)不可能等于 "0 1 0 ",op_host UT 在 EXPECT_EQ(tilingDataResult, expectTilingData) 处稳定失败,run.sh 第 2 行 set -e 会导致整个 UT 流程中断。另外第 31-34 行的 TODO 也承认该期望值来自初始模板未更新。
修复:按实现的真实输出更新期望串(或先用 ExecuteTiling 打印实际值再回填),不要保留占位值。
建议:用实现真实输出更新 expectTilingData(totalNum / blockFactor / ubFactor 三个 int64 的十进制值,空格分隔),或改为传 EMPTY_EXPECT_TILING_DATA 暂时跳过该字段校验,避免 UT 必然失败。


🟡 Medium Priority
变更行 tests/ut/run.sh:5 将 ENABLE_INNER_KERNEL_UT 默认置为 OFF 传入配置,但第 10 行无条件执行 "$BUILD/op_kernel/leakyrelu_op_kernel_ut",第 8-11 行也无条件先 generate 再 verify 内核样本。
失败模式:若 tests/ut/CMakeLists.txt 用该变量控制 op_kernel 子目录是否 add_subdirectory(变量名为 "INNER_KERNEL_UT",正是内核 UT 的开关),则默认 OFF 时该可执行文件根本不生成,第 10 行在 set -e 下以 "No such file or directory" 直接终止脚本,kernel 侧 UT 完全不会被验证;反之若该变量实际不控制任何东西,它就是一个误导性的死选项。这两种情况都属于脚本与构建约定的不一致。
修复:要么把第 10 行的执行也用同一开关包裹(if [ "${ENABLE_INNER_KERNEL_UT:-OFF}" = "ON" ]; then ... fi),要么默认值改为 ON 并显式说明内核 UT 一定构建。
建议:让 ENABLE_INNER_KERNEL_UT 的默认值与后续行为一致:或默认 ON,或把第 10、11 行的内核 UT 执行与样本 verify 同样用 if [ "${ENABLE_INNER_KERNEL_UT:-OFF}" = "ON" ] 包裹,避免默认配置下执行不存在的可执行文件。


DingLabChina
团队信息
成员
算子: exercises
PyTorch 原生算子:torch.nn.functional.leaky_relu,即 y = max(x, 0) + negative_slope * min(x, 0),负半轴斜率 negative_slope 默认 0.01
参考文档:https://docs.pytorch.org/docs/2.13/generated/torch.nn.functional.leaky_relu.html