Issue: 工具链更新后两处回归阻断全部编译(BLayout::ND2M 笔误 + B.DIM 立即数 matcher 拒收)
- 日期: 2026-09-06
- 组件: linx toolchain (LLVM LinxV5 后端 + Linx-TileOP-API 头文件)
- 类型: 功能性 bug(编译期/汇编期,非代码生成质量)
- 严重度: High(阻断所有包含 TileOP 头文件的翻译单元;参考内核同样无法编译)
摘要
一次工具链更新(output/ 重建)引入两个独立回归,二者叠加后 任何 使用
tileop-api 的内核都无法编译。更新前同一份源码可干净编译并产出 ELF。
| # |
回归 |
层级 |
触发条件 |
现态 |
| 1 |
BLayout::ND2M16/BLayout::ND2M32 枚举作用域笔误 |
头文件(phase-1 名查找) |
任何 TU 包含 template_asm.hpp |
安装副本已临时改对;源未改 |
| 2 |
B.DIM <立即数> 被汇编器 matcher 拒收 |
汇编器后端 / 头文件约束 |
任何 TLOAD/TMULS 等模板的 valid_row/valid_col 为编译期常量 |
未修,无法在内核侧规避 |
环境
- 编译器:
output/linx_blockisa_llvm_musl/bin/clang++(clang 15.0.4,target linx64v5-unknown-linux-musl)
- 头文件根:
output/linx_blockisa_llvm_musl/lib/clang/15.0.4/include/tileop-api/
- 受影响内核(复现用):
SuperNPUBench/benchmark/one-level-arch/kernels/multi_thread/fa/fa_opt.hpp
SuperNPUBench/benchmark/one-level-arch/test/kernel/multi_thread/fa/src/fa_2d_unroll_gmma.cpp(参考内核,未改动)
- 典型编译命令:
export COMPILER_DIR=/Users/blacktraker/Programming/gitproj/DV4/linx-toolchain-build/output/linx_blockisa_llvm_musl/bin
make -C benchmark/one-level-arch/test/kernel/multi_thread/fa diss \
TESTCASE=fa_opt|fa_2d_unroll_gmma \
COMPILER_DIR="$COMPILER_DIR" FA_MODE=FP32_VECFP32 \
Sq=256 Skv=256 Tm=128 Tk=128 X_dim=1 Y_dim=1 OBJ_ROOT=/tmp/<build>
现象
回归 1:BLayout::ND2M16 / BLayout::ND2M32 笔误
template_asm.hpp:8949-8950,TIMG2COL 模板内:
[Layout] "i"(tile_shape_out::BFractal == BLayout::CubeM16 ?
BLayout::ND2M16 : BLayout::ND2M32),
ND2M16/ND2M32 实为无作用域枚举 LayoutCvtEnum(common/layout.hpp:64)的成员,不属于 enum class BLayout(layout.hpp:35,成员仅 RowMajor/ColMajor/CubeM16/CubeM32/CubeN8)。此处误抄了条件里 BLayout::CubeM16 的前缀。
报错(fa_opt 根本未调用 TIMG2COL 也被拦下):
template_asm.hpp:8950:26: error: no member named 'ND2M16' in 'pto::BLayout'; did you mean simply 'ND2M16'?
template_asm.hpp:8950:44: error: no member named 'ND2M32' in 'pto::BLayout'; did you mean simply 'ND2M32'?
layout.hpp:79:3: note: 'ND2M16' declared here // LayoutCvtEnum
layout.hpp:78:3: note: 'ND2M32' declared here // LayoutCvtEnum
为什么所有 TU 都炸:BLayout::ND2M16 是不依赖模板参数的受限名,按
[temp.nondep] 在模板定义点(phase-1)即做名字查找与绑定,不要求 TIMG2COL
被实例化。因此任何包含该头文件的翻译单元都会在 phase-1 失败。
回归 2:B.DIM <立即数> Match Instruction Error
修补回归 1 后编译继续,撞到汇编期错误。来自工具链自身的 TLOAD/TMULS
模板,fa_opt 与参考内核 fa_2d_unroll_gmma 同样失败、模式一致:
template_asm.hpp:2027:6: error: Match Instruction Error! "B.DIM %[VCOL], 0, ->lb0\n"
<inline asm>:2:1: B.DIM 128, 0, ->lb0 # TLOAD,valid_col=128
template_asm.hpp:2028:6: error: Match Instruction Error! "B.DIM %[VROW], 0, ->lb1\n"
<inline asm>:3:1: B.DIM 128, 0, ->lb1 # TLOAD,valid_row=128
template_asm.hpp:8062:6: error: Match Instruction Error! "B.DIM %2, 0, ->lb0\n"
<inline asm>:2:1: B.DIM 128, 0, ->lb0 # TMULS,valid_col=128
template_asm.hpp:8063:6: error: Match Instruction Error! "B.DIM %3, 0, ->lb1\n"
<inline asm>:3:1: B.DIM 32, 0, ->lb1 # TMULS,valid_row=32 (tW: 32×128)
template_asm.hpp:169/170 B.DIM 128/32 ... # 另一处 TLOAD 变体
被拒收的立即数值都是内核 tile 的有效维度(128、32),更新前同样源码可编译。
根因分析
回归 1
BLayout(enum class)与 LayoutCvtEnum(无作用域 enum)是两个不同枚举,
ND2M16/ND2M32 属于后者。template_asm.hpp:8950 把它们误写成 BLayout::
前缀(疑似从条件 BLayout::CubeM16 复制粘贴)。属头文件笔误,phase-1 命名查找
失败,阻断所有翻译单元。
回归 2
TLOAD(template_asm.hpp:2015-2041)等模板把 tile 的 valid_col/valid_row
以 "ri"(寄存器或立即数)约束送入 B.DIM 第一操作数:
const size_t valid_col = dst.GetValidCol();
const size_t valid_row = dst.GetValidRow();
asm volatile(
"BSTART.TLSU TLOAD, %D[SrcType]\n"
"B.DIM %[VCOL], 0, ->lb0\n" // VCOL = "ri"(valid_col)
"B.DIM %[VROW], 0, ->lb1\n" // VROW = "ri"(valid_row)
"B.DIM zero, %c[COL], ->lb2\n" // COL = "i"(常量, 用 %c 修饰)
...
当 valid_col/valid_row 为编译期常量(fa_opt 的 128×128 Q/K/V、32×128 的
tW 等),LLVM 常量折叠把它物化为裸立即数,于是汇编文本变成
B.DIM 128, 0, ->lb0。新汇编器的指令 matcher 不再接受 B.DIM <立即数> 形式,
报 Match Instruction Error。
对照:同一指令第三行的 B.DIM zero, %c[COL], ->lb2 用 %c 修饰符输出常量
(B.DIM zero, 128, ->lb2),不报错——说明问题不在"128 这个值越界",
而在"第一操作数位置出现裸立即数"这条匹配路径被新 matcher 移除/未实现。
波及面
B.DIM %<...> + "ri" 模式在 template_asm.hpp 中有 284 处,其中约
235 处带 "ri" 约束。这是工具链级约定,波及 TLOAD/TSTORE/TCVT/
TMULS/TROWMAX/TROWSUM 等几乎所有 tile 操作模板。任何 valid 维度为
编译期常量的内核都会命中。
- 参考内核
fa_2d_unroll_gmma(未改动)在新编译器下报与 fa_opt 完全相同
的 B.DIM 128/32 错误,证明为工具链全局回归,与 fa_opt 实现无关。
责任归属
- 回归 1:Linx-TileOP-API 头文件笔误(
template_asm.hpp:8950),属明确 bug。
- 回归 2:新汇编器后端的
B.DIM 指令 matcher 不接受第一操作数为立即数;
与头文件 "ri" 约束共同作用触发。是后端 matcher 回归,还是头文件约束
本应为 "r"(强制寄存器)而旧汇编器过于宽松,需工具链维护者定夺——但
从用户内核侧无法规避(相关 B.DIM 全在工具链模板里)。
临时处理与建议修复
回归 1(已临时改对安装副本)
- 临时:已把
output/.../include/tileop-api/jcore/template_asm.hpp:8950 的
BLayout::ND2M16/BLayout::ND2M32 改为 LayoutCvtEnum::ND2M16/
LayoutCvtEnum::ND2M32。仅改了安装产物,重编工具链会被覆盖。
- 建议源侧修复:在工具链源对应文件作同样一行修正即可。
回归 2(未修,无法在内核侧规避)
二选一,由工具链维护者判定:
- 汇编器后端恢复
B.DIM 第一操作数接受立即数(恢复旧 matcher 行为);
- 或将相关模板的
"ri" 约束改为 "r"(强制寄存器,规避物化为立即数)。
该改动面大(约 235 处),但机械一致;需评估对代码生成质量的影响。
在回归 2 修复前,建议回滚本次工具链更新至可编译状态,以恢复日常构建与
gfrun 回归。
复现
cd /Users/blacktraker/Programming/gitproj/DV4/SuperNPUBench
export COMPILER_DIR=/Users/blacktraker/Programming/gitproj/DV4/linx-toolchain-build/output/linx_blockisa_llvm_musl/bin
# fa_opt(优化 FA 内核)
make -C benchmark/one-level-arch/test/kernel/multi_thread/fa diss \
TESTCASE=fa_opt COMPILER_DIR="$COMPILER_DIR" FA_MODE=FP32_VECFP32 \
Sq=256 Skv=256 Tm=128 Tk=128 X_dim=1 Y_dim=1 OBJ_ROOT=/tmp/fa_opt_v4
# 参考内核(未改动,更新前可编译)
make -C benchmark/one-level-arch/test/kernel/multi_thread/fa \
TESTCASE=fa_2d_unroll_gmma COMPILER_DIR="$COMPILER_DIR" FA_MODE=FP32_VECFP32 \
Sq=256 Skv=256 Tm=128 Tk=128 X_dim=1 Y_dim=1 OBJ_ROOT=/tmp/fa_ref_v4
# 观察:
# - 回归1:template_asm.hpp:8950 no member named 'ND2M16' in 'pto::BLayout'
# - 回归2:template_asm.hpp:2027/2028/8062/8063/169/170 Match Instruction Error
# + <inline asm> B.DIM 128/32, 0, ->lb0|lb1
# - 两者均出现在 fa_opt 与 fa_2d_unroll_gmma 上,模式一致。
附:fa_opt 侧未改动
fa_opt.hpp / fa_opt.cpp 与上一可编译状态(OBJ_ROOT=/tmp/fa_opt_v3,旧编译器)
一致,未为本次更新做任何调整;上一状态编译干净,反汇编已验证
TMATMUL=8, TCVT=0, B.TRB=0(无 transpose_b)。本次失败完全由工具链回归造成。
Issue: 工具链更新后两处回归阻断全部编译(BLayout::ND2M 笔误 + B.DIM 立即数 matcher 拒收)
摘要
一次工具链更新(
output/重建)引入两个独立回归,二者叠加后 任何 使用tileop-api的内核都无法编译。更新前同一份源码可干净编译并产出 ELF。BLayout::ND2M16/BLayout::ND2M32枚举作用域笔误template_asm.hppB.DIM <立即数>被汇编器 matcher 拒收TLOAD/TMULS等模板的 valid_row/valid_col 为编译期常量环境
output/linx_blockisa_llvm_musl/bin/clang++(clang 15.0.4,targetlinx64v5-unknown-linux-musl)output/linx_blockisa_llvm_musl/lib/clang/15.0.4/include/tileop-api/SuperNPUBench/benchmark/one-level-arch/kernels/multi_thread/fa/fa_opt.hppSuperNPUBench/benchmark/one-level-arch/test/kernel/multi_thread/fa/src/fa_2d_unroll_gmma.cpp(参考内核,未改动)现象
回归 1:
BLayout::ND2M16/BLayout::ND2M32笔误template_asm.hpp:8949-8950,TIMG2COL模板内:ND2M16/ND2M32实为无作用域枚举LayoutCvtEnum(common/layout.hpp:64)的成员,不属于enum class BLayout(layout.hpp:35,成员仅RowMajor/ColMajor/CubeM16/CubeM32/CubeN8)。此处误抄了条件里BLayout::CubeM16的前缀。报错(fa_opt 根本未调用
TIMG2COL也被拦下):为什么所有 TU 都炸:
BLayout::ND2M16是不依赖模板参数的受限名,按[temp.nondep] 在模板定义点(phase-1)即做名字查找与绑定,不要求
TIMG2COL被实例化。因此任何包含该头文件的翻译单元都会在 phase-1 失败。
回归 2:
B.DIM <立即数>Match Instruction Error修补回归 1 后编译继续,撞到汇编期错误。来自工具链自身的
TLOAD/TMULS模板,fa_opt 与参考内核
fa_2d_unroll_gmma同样失败、模式一致:被拒收的立即数值都是内核 tile 的有效维度(128、32),更新前同样源码可编译。
根因分析
回归 1
BLayout(enum class)与LayoutCvtEnum(无作用域 enum)是两个不同枚举,ND2M16/ND2M32属于后者。template_asm.hpp:8950把它们误写成BLayout::前缀(疑似从条件
BLayout::CubeM16复制粘贴)。属头文件笔误,phase-1 命名查找失败,阻断所有翻译单元。
回归 2
TLOAD(template_asm.hpp:2015-2041)等模板把 tile 的valid_col/valid_row以
"ri"(寄存器或立即数)约束送入B.DIM第一操作数:当
valid_col/valid_row为编译期常量(fa_opt 的 128×128 Q/K/V、32×128 的tW 等),LLVM 常量折叠把它物化为裸立即数,于是汇编文本变成
B.DIM 128, 0, ->lb0。新汇编器的指令 matcher 不再接受B.DIM <立即数>形式,报
Match Instruction Error。对照:同一指令第三行的
B.DIM zero, %c[COL], ->lb2用%c修饰符输出常量(
B.DIM zero, 128, ->lb2),不报错——说明问题不在"128 这个值越界",而在"第一操作数位置出现裸立即数"这条匹配路径被新 matcher 移除/未实现。
波及面
B.DIM %<...>+"ri"模式在template_asm.hpp中有 284 处,其中约235 处带
"ri"约束。这是工具链级约定,波及TLOAD/TSTORE/TCVT/TMULS/TROWMAX/TROWSUM等几乎所有 tile 操作模板。任何 valid 维度为编译期常量的内核都会命中。
fa_2d_unroll_gmma(未改动)在新编译器下报与 fa_opt 完全相同的
B.DIM 128/32错误,证明为工具链全局回归,与 fa_opt 实现无关。责任归属
template_asm.hpp:8950),属明确 bug。B.DIM指令 matcher 不接受第一操作数为立即数;与头文件
"ri"约束共同作用触发。是后端 matcher 回归,还是头文件约束本应为
"r"(强制寄存器)而旧汇编器过于宽松,需工具链维护者定夺——但从用户内核侧无法规避(相关
B.DIM全在工具链模板里)。临时处理与建议修复
回归 1(已临时改对安装副本)
output/.../include/tileop-api/jcore/template_asm.hpp:8950的BLayout::ND2M16/BLayout::ND2M32改为LayoutCvtEnum::ND2M16/LayoutCvtEnum::ND2M32。仅改了安装产物,重编工具链会被覆盖。回归 2(未修,无法在内核侧规避)
二选一,由工具链维护者判定:
B.DIM第一操作数接受立即数(恢复旧 matcher 行为);"ri"约束改为"r"(强制寄存器,规避物化为立即数)。该改动面大(约 235 处),但机械一致;需评估对代码生成质量的影响。
在回归 2 修复前,建议回滚本次工具链更新至可编译状态,以恢复日常构建与
gfrun 回归。
复现
附:fa_opt 侧未改动
fa_opt.hpp/fa_opt.cpp与上一可编译状态(OBJ_ROOT=/tmp/fa_opt_v3,旧编译器)一致,未为本次更新做任何调整;上一状态编译干净,反汇编已验证
TMATMUL=8, TCVT=0, B.TRB=0(无 transpose_b)。本次失败完全由工具链回归造成。