已关闭
[Bug] foreach_addcmul_scalar int32 经 float24 往返丢失低位(16777217→16777216) #4519
Agent-Bug-Hunter创建于 8月3日关闭于 25 天前
8月4日 将 east_yang 设为负责人
yuning_chen
8月4日 评论:
8月4日 评论:
您好,感谢反馈,问题已收到,当前 @east_yang 正在跟踪处理。


25 天前 关闭了 issue
25 天前 添加了label:resolved
您好,感谢反馈,问题已收到,当前 @east_yang 正在跟踪处理。


摘要
foreach_addcmul_scalar对 int32 路径经 float 往返导致大于 2^24 的整数低位丢失(16777217→16777216)。Ascend910B3 真机 exit 42。本地 draft,未远程提交。环境
e16ef626bb3acann-ops-nn-cann_scan_repro_linux-*.run→$ASCEND_HOME_PATH/vendors/cann_scan_repro_nn为什么认为这是问题:期望 vs 实际
输入(与复现用例一致)
期望行为
实际行为(aclnn / 源码编译安装路径)
对比结论
根因
exit 42: expect 16777217 got 16777216 via float32 intermediate.
远程未提交。
(详细行号见同目录 FINDING.md 与算子源码;归档时已通过 aclnn exit 42 确认。)
为何确认跑的是源码编译内核(非 built-in)
source $ASCEND_HOME_PATH/vendors/cann_scan_repro_nn/bin/set_env.bash,ASCEND_CUSTOM_OPP_PATH指向该 vendor。op_api/op_impl,而非仅依赖 CANN built-in 同名算子(见repro_manifest.json中built_from_source=true)。完整复现过程
占位符:
$ASCEND_HOME_PATH/$OPS_NN/$REPRO_ROOT。步骤 1:环境使能
source <CANN安装目录>/ascend-toolkit/set_env.sh步骤 2:源码编译单算子包
cd "$OPS_NN" bash build.sh --pkg --soc=ascend910b --ops=foreach_addcmul_scalar \ --vendor_name=cann_scan_repro --no_force -j8成功标志:生成
$OPS_NN/build_out/cann-ops-nn-cann_scan_repro_linux-*.run步骤 3:安装并切换到自定义包
bash $OPS_NN/build_out/cann-ops-nn-cann_scan_repro_linux-*.run \ --quiet --install-path="$ASCEND_HOME_PATH" source "$ASCEND_HOME_PATH/vendors/cann_scan_repro_nn/bin/set_env.bash" echo "ASCEND_CUSTOM_OPP_PATH=$ASCEND_CUSTOM_OPP_PATH"步骤 4:放置复现源码与执行脚本
(本 draft 目录内已含上述文件,可直接作为
$REPRO_ROOT。)步骤 5:编译并运行
cd "$REPRO_ROOT" chmod +x build_and_run_repro.sh bash build_and_run_repro.sh --vendor cann_scan_repro --src test_aclnn_foreach_addcmul_scalar_int32.cpp # 退出码 42 = BUG_REPRODUCED成功复现标志:进程退出码 42。
实际运行摘录
复现用例源码
文件名:
test_aclnn_foreach_addcmul_scalar_int32.cpp/** * FEACMS-INT32 — foreach_addcmul_scalar int32 scalar float24 roundtrip. * out = x1 + scalar * x2 * x3 * int32: x1=0, x2=1, x3=1; scalar=16777217 (=2^24+1) * expect exact 16777217; buggy float path → 16777216 * N=1 (not TP-007 list-cap). */ #include <cstdint> #include <cstdio> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_foreach_addcmul_scalar.h" #define CHECK_RET(cond, return_expr) \ do { if (!(cond)) { return_expr; } } while (0) #define LOG_PRINT(message, ...) do { printf(message, ##__VA_ARGS__); } while (0) static constexpr int32_t kScalar = 16777217; // 2^24+1 static constexpr int32_t kExpect = 16777217; static constexpr int32_t kBuggy = 16777216; // float mantissa truncate static int Init(int32_t deviceId, aclrtStream* stream) { auto ret = aclInit(nullptr); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); ret = aclrtSetDevice(deviceId); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); ret = aclrtCreateStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); return 0; } static int CreateInt32ScalarTensor(int32_t hostVal, void** deviceAddr, aclTensor** tensor) { std::vector<int64_t> shape = {1}; std::vector<int64_t> strides = {1}; auto ret = aclrtMalloc(deviceAddr, sizeof(int32_t), ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); ret = aclrtMemcpy(*deviceAddr, sizeof(int32_t), &hostVal, sizeof(int32_t), ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy H2D failed. ERROR: %d\n", ret); return ret); *tensor = aclCreateTensor(shape.data(), shape.size(), ACL_INT32, strides.data(), 0, ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); CHECK_RET(*tensor != nullptr, LOG_PRINT("aclCreateTensor failed\n"); return 1); return 0; } int main() { int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, &stream); CHECK_RET(ret == ACL_SUCCESS, return 1); LOG_PRINT("FEACMS-INT32: N=1 int32 addcmul; scalar=%d expect=%d buggy_float=%d\n", kScalar, kExpect, kBuggy); LOG_PRINT("formula: out = x1 + scalar * x2 * x3 = 0 + %d * 1 * 1\n", kScalar); void *dX1 = nullptr, *dX2 = nullptr, *dX3 = nullptr, *dOut = nullptr, *dScalar = nullptr; aclTensor *tX1 = nullptr, *tX2 = nullptr, *tX3 = nullptr, *tOut = nullptr, *tScalar = nullptr; ret = CreateInt32ScalarTensor(0, &dX1, &tX1); CHECK_RET(ret == 0, return 1); ret = CreateInt32ScalarTensor(1, &dX2, &tX2); CHECK_RET(ret == 0, return 1); ret = CreateInt32ScalarTensor(1, &dX3, &tX3); CHECK_RET(ret == 0, return 1); ret = CreateInt32ScalarTensor(-999, &dOut, &tOut); CHECK_RET(ret == 0, return 1); ret = CreateInt32ScalarTensor(kScalar, &dScalar, &tScalar); CHECK_RET(ret == 0, return 1); aclTensor* arrX1[] = {tX1}; aclTensor* arrX2[] = {tX2}; aclTensor* arrX3[] = {tX3}; aclTensor* arrOut[] = {tOut}; aclTensorList* lX1 = aclCreateTensorList(arrX1, 1); aclTensorList* lX2 = aclCreateTensorList(arrX2, 1); aclTensorList* lX3 = aclCreateTensorList(arrX3, 1); aclTensorList* lOut = aclCreateTensorList(arrOut, 1); CHECK_RET(lX1 && lX2 && lX3 && lOut, LOG_PRINT("aclCreateTensorList failed\n"); return 1); uint64_t workspaceSize = 0; aclOpExecutor* executor = nullptr; ret = aclnnForeachAddcmulScalarGetWorkspaceSize(lX1, lX2, lX3, tScalar, lOut, &workspaceSize, &executor); LOG_PRINT("GetWorkspaceSize ret=%d wsSize=%llu\n", ret, (unsigned long long)workspaceSize); if (ret != ACL_SUCCESS) { LOG_PRINT("\nVERDICT: NO_REPRO / blocked (GetWorkspaceSize ret=%d)\n", ret); // cleanup abbreviated via finalize path aclDestroyTensorList(lX1); aclDestroyTensorList(lX2); aclDestroyTensorList(lX3); aclDestroyTensorList(lOut); aclDestroyTensor(tX1); aclDestroyTensor(tX2); aclDestroyTensor(tX3); aclDestroyTensor(tOut); aclDestroyTensor(tScalar); aclrtFree(dX1); aclrtFree(dX2); aclrtFree(dX3); aclrtFree(dOut); aclrtFree(dScalar); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; } void* workspaceAddr = nullptr; if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("workspace malloc failed %d\n", ret); return 1); } ret = aclnnForeachAddcmulScalar(workspaceAddr, workspaceSize, executor, stream); LOG_PRINT("aclnnForeachAddcmulScalar ret=%d\n", ret); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("launch failed\n"); return 1); ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("sync failed\n"); return 1); int32_t actual = 0; ret = aclrtMemcpy(&actual, sizeof(int32_t), dOut, sizeof(int32_t), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("D2H failed\n"); return 1); LOG_PRINT("\n| field | expected | actual |\n|-------|----------|--------|\n"); LOG_PRINT("| out[0] | %d | %d |\n", kExpect, actual); LOG_PRINT("| buggy_float24 | %d | (ref) |\n", kBuggy); if (workspaceAddr) aclrtFree(workspaceAddr); aclDestroyTensorList(lX1); aclDestroyTensorList(lX2); aclDestroyTensorList(lX3); aclDestroyTensorList(lOut); aclDestroyTensor(tX1); aclDestroyTensor(tX2); aclDestroyTensor(tX3); aclDestroyTensor(tOut); aclDestroyTensor(tScalar); aclrtFree(dX1); aclrtFree(dX2); aclrtFree(dX3); aclrtFree(dOut); aclrtFree(dScalar); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); if (actual == kBuggy && actual != kExpect) { LOG_PRINT("\nVERDICT: BUG_REPRODUCED (exit 42) float24 roundtrip\n"); return 42; } if (actual == kExpect) { LOG_PRINT("\nVERDICT: NO_REPRO (exact int32 result)\n"); return 0; } LOG_PRINT("\nVERDICT: unexpected actual=%d → exit 1\n", actual); return 1; }执行脚本
文件名:
build_and_run_repro.sh#!/usr/bin/env bash set -euo pipefail : "${ASCEND_HOME_PATH:?请先 source CANN set_env.sh}" ROOT="$(cd "$(dirname "$0")" && pwd)" SRC="$ROOT/test_aclnn_foreach_addcmul_scalar_int32.cpp" BIN="$ROOT/test_aclnn_foreach_addcmul_scalar_int32" INC="${ASCEND_HOME_PATH}/aarch64-linux/include" LIB="${ASCEND_HOME_PATH}/aarch64-linux/lib64" [[ -d "$INC" ]] || INC="${ASCEND_HOME_PATH}/include" [[ -d "$LIB" ]] || LIB="${ASCEND_HOME_PATH}/lib64" echo "[build] g++ $SRC" g++ -std=c++17 -O2 "$SRC" \ -I"$INC" -I"$INC/aclnnop" \ -L"$LIB" \ -Wl,-rpath,"$LIB" \ -lascendcl -lnnopbase -lopapi -lopapi_nn \ -o "$BIN" echo "[run] $BIN" set +e "$BIN" RC=$? set -e echo "[exit] $RC (42=BUG_REPRODUCED, 0=NO_REPRO, 1=harness error)" exit "$RC"修复建议
按文档/torch 参考语义修正计算或 tiling;补充合法输入单测与期望对照。
人工复核说明
Agent 复现结果供参考;语义边界(是否允许 wrap、文档是否声明调用方契约)以人工结论与产品规范为准。