已开启
[Requirement|需求建议]: 支持 IndexExpr 和 Arange Autofusion #302
Jett_Woo创建于 25 天前
14 天前 关联了pull request:feat: 支持 IndirectLoad Load-Transpose 布局并完善 SIMD/SIMT codegen [backport 9.2.0]
14 天前 关联了pull request:feat: 支持 IndirectLoad Load-Transpose 布局并完善 SIMD/SIMT codegen [backport 9.2.0]


简介
目的
本文档描述 IndexExpr 与 Arange Autofusion 的完整需求设计(Issue #302:支持
IndexExpr 和 Arange Autofusion),覆盖两个算子的端到端能力与两期合入:
feat: support IndexExpr and Arange autofusion,已合入 develop,2026-09-05):建立 IndexExpr/Arange 的 ASCIR 注册、Python 接口、IndexExpr 标量消费链路、VF Arange codegen 与调度准入等基础能力。fix: complete Arange fallback path,关联 Issue #302):补齐退化轴 Arange 广播透传、根图 Arange codegen 回退路径与 VF 准入门控,扩展 IndexExpr 标量块物化,并修复 VF 混合 dtype 掩码错位、Store DMA 方向错误与 Python 绑定引用计数缺陷。需求是双主体的:IndexExpr 负责符号标量(尺寸/索引表达式的整型物化与标量消费),Arange 负责等差序列(向量生产与广播扩维);两者经 Broadcast 汇入同一 elementwise 融合链。
目标读者:Autofuse 前端接入(TorchInductor lowering)、图优化与 codegen 维护者、测试开发者。期望输出:合入 develop 的完整能力 + 正式 UT/ST + 真机验证闭环。
范围
包含(两期合并):
autofuse/ascir/generator、autofuse/v35/ascir/generator)autofuse/v35/optimize、autofuse/optimize)autofuse/v35/codegen、autofuse/codegen):MicroAPI/VF 双路径 + 根图 ArangeApiCall 回退autofuse/compiler/python/ascir_api.py)autofuse/compiler/py_module)不包含:
tool/、temp/(本地资产,不入库)总体概述
软件概述
项目介绍
Autofuse 前端(TorchInductor 接入层)把符号计算拆解为 **IndexExpr(标量表达式)
ops.index_expr(expr, dtype)语义——把符号尺寸/索引表达式(如
s27*s77、s0 + 2*k)物化为一个整型标量值,参与标量算术(Add/Minimum/Maximum)、比较(Lt/Le/Gt/Ge/Eq/Ne)或经 Broadcast 扩为向量。
base + k*step),组合索引数学(如行主序线性索引
cols*p0 + p1、triu 掩码的p2 - p1)拆解为退化轴 Arange +Broadcast + Add。
两者在 VF(VectorFunc)融合链中分别承担"值参数"与"向量生产"角色:IndexExpr
值经 VF 函数参数传入 MicroAPI 标量变体(如
Adds),Arange 在 VF 内经Reg::Arange生成序列后作为张量输入参与计算。需求按两期演进:
PR 1980 之前存在三个真机复现缺陷:① Pattern B(尾退化
[4,1]/[1,0])在旧"展开方案"下 codegen 必然失败;② VF 嵌套循环掩码 dtype 未统一,混合 dtype 子图真机上每 4 个元素损坏 1 个;③ 根图 Arange 无 codegen 注册(ApiCall 名为空串),三类回退场景全部无法出码。产品环境介绍
autofusePython 包(pyautofuse.so + ascir_api.py),Python 3.10+(CI 为 3.10.2)。软件功能
IndexExpr 能力:
expr承载完整Expression(动态SizeExpr及 FloorDiv/Mod/Min/Max 组合),v1/v2 注册输出 dtype 限定DT_INT32/DT_INT64;根图物化为 kernel 内具名符号标量(tiling 参数引用),非普通 UB tensor。Add→Adds),其余位置经显式 Broadcast 扩为向量后消费。tiler.Size表达式(1980)。IndexExpr(int64) → Broadcast → Cast(目标浮点),Cast 必须在向量域;运行时 dtype 不确定走 guard fallback(规划项float_indexexpr_cast_mulRDV 用例)。Arange 能力:
base/step均为 Expression(支持动态 base、动态 step、非 1/0/负 step),输出 int32/int64。ascir_api.arange(...)提供 axis/size/stride 逻辑 view 构图入口(与IndexExpr入口对称)。Reg::Arange生成数值序列;支持跨 vector block、外层调度 offset(outer-for 逻辑偏移)、高维(最多 4 个 vectorized_axis)。ArangeApiCall,显式循环物化等差序列,物理写偏移与逻辑取值偏移分离(1980)。链路修复(PR 1980,双主体共用):
设计约束
vectorized_stride必须静态等于 1(两期共同约束,1961 确立)。SizeExprrichcompare 协议(新引用、NotImplemented);pyautofuse.so 与 Python 3.10/3.12 双版本构建均需通过。假设和依赖关系
Reg::Arange只能表达单条连续等差序列——烘焙广播必须留在根图的前提。expr恒为整数语义表达式(inductor size/index 语义按 int64 管理)。ApplyGraphSchedAxisToNodes(图级 sched 统一提升)为基线行为,一级扩维要求由此推导(已实测验证)。需求分析与设计
整体介绍
数据流与关键模块边界(两期合并视图):
flowchart LR subgraph FE["前端构图 (ascir_api)"] A1["IndexExpr API (expr: SizeExpr)"] A2["arange API<br/>退化视图校验 (位置无关规则)"] end subgraph REG["ASCIR 注册 (PR 1961)"] R1["v1 IndexExpr<br/>v2 Arange base/step Expression"] end subgraph OPT["图优化"] B1["NormalizeArangeBroadcastViews<br/>(PR 1980, 合轴前)"] B2["RefineEnableVFFlag<br/>Arange/Cast VF 门控 (PR 1980)"] B3["UnAlignmentStrategy<br/>Arange 消费对齐 (两期演进)"] end subgraph CG["Codegen"] C0["IndexExpr: 符号常量/标量块<br/>+ VF 值参数 (1961+1980)"] C1["VF: Reg::Arange + scalar 参数<br/>(PR 1961) + 掩码 dtype 统一 (PR 1980)"] C2["ArangeApiCall 根图回退<br/>(PR 1980 新增)"] C3["Store DMA / Cast 修复 (PR 1980)"] end A1 --> B2 A2 --> B1 --> B2 --> B3 --> C0 & C1 & C2 & C3 R1 -.注册支撑.-> A1 & A2两期边界划分:PR 1961 的 VF 内 Arange 能力(逻辑 offset、outer-for、参数隔离)在 PR 1980 中保持不变;PR 1980 的门控把无法安全表达 VF 的 Arange 形态(烘焙广播、控制边、UBFuse)导向新根图路径,两路径互补。
功能需求
功能需求 1:IndexExpr Expression 物化与注册(PR 1961)
IndexExpr.expr从整数 attr 升级为完整Expression,可承载动态SizeExpr及组合(s27*s77等);v1/v2 均注册,输出 dtype 限定DT_INT32/DT_INT64。根图物化为 kernel 内具名符号标量(非 UB tensor),值随 tiling 参数在 host 侧解析。符号表达式(常量、size vars、循环变量线性式、FloorDiv/Mod/Min/Max 组合)。
v2 注册表登记 dtype 白名单(
ascir_builtin_ops_v2.cpp);infer_dtypes校验;codegen 侧TPipe::AddTensor(output, expr, name)注册 Expression 常量并区分字符串常量路径(值为 0 的 Expression 仍为有效表达式)。根图有命名 const 标量;消费者经
GetScalarValue()取得生成后的具名变量(const_value为空时回退 tensor 变量名),或在显式 Broadcast 后取得向量。功能需求 2:IndexExpr scalar-like 消费边协议(PR 1961)
每条 IndexExpr→消费者的边必须确定走标量还是张量协议,不能因消费者"属于 elementwise"整体放行;多消费者时各边可有不同 Broadcast view,不把单一消费者的 view 复制给所有用途。
消费者算子类型、输入位置、其他输入是否 scalar、输出 view、所选普通/VF 实现。
按消费边位置分类(协议表见 AL-9):Add/Minimum/Maximum 允许单个 scalar 输入(MicroBinaryScalar 自动交换到第二位、API 加
s后缀);六种比较仅第二输入可识别 scalar;其余位置(比较第一输入、Store 数据输入等)不支持直连,需上游显式 Broadcast。白名单判定统一为IsScalarNodeType(Scalar | ScalarData | IndexExpr)。合法 scalar call(
Adds/Muls等 MicroAPI 标量变体)或显式 Broadcast 后的向量消费;不支持位置保留必要 Broadcast 或拒绝,不悄悄交换非交换律操作参数。功能需求 3:IndexExpr 标量块物化(PR 1980)
根图 IndexExpr 纳入与 Scalar 相同的 blk tensor(32B 对齐标量块)物化链,索引消费链可直接引用对齐标量块。
根图 IndexExpr 节点及其输出张量调度/tiling 元数据。
ParseScalarNeedGenBlkTensors判定纳入 IndexExpr;BlkTensorAllocAndInit常量值回退(const_value为空时用tiler.Size(const_value_expr, true)生成 tiling 参数引用);ParseGraph的 IndexExpr 分支补ParseOptimizeInfo(此前只 AddTensor 不解析优化信息,后续 tiling 查不到该张量)。根图 IndexExpr 生成合法的 blk tensor 初始化代码;符号值 Scalar 不再产出空串非法 C++。
功能需求 4:IndexExpr 浮点消费协议(前端契约,规划项)
ops.index_expr(expr, float_dtype)不能直接物化(dtype 白名单仅整型);前端拆解为IndexExpr(int64) → Broadcast → Cast(目标浮点),Cast 必须在 Broadcast 之后(向量域)——IndexExpr 输出无 axis/repeats(scalar-like),向量算子的 Cast 需要 view,不存在"Broadcast 前标量域 Cast"的合法形态。整数语义表达式、目标浮点 dtype(float16/bfloat16/float32)。
前端构图改写(外部仓);运行时 dtype 无法静态确定时走既有 dtype guard fallback(该融合点不进入 AF)。
infer_dtypes()通过的合法 ASCIR 图;等价 RDV 用例float_indexexpr_cast_mul(规划,形状[32,64]/[32,65]/[2,127]对拍 eager)。功能需求 5:Arange v2 注册与前端 API(PR 1961 + 1980 收紧)
v2 注册
Arange(零输入),base/step均为 Expression;Pythonascir_api.arange(...)提供逻辑 view(axis/size/stride)构图入口。PR 1980 收紧:任意位置stride==0 且 size!=1构图期拒绝并提示改法(use 1D Arange followed by explicit Broadcast)。dtype(int32/int64)、base/step(SizeExpr)、axis/size/stride(等长三列表)。注册表校验 dtype;前端校验 view 一致性 + 位置无关零 stride 规则。
合法 Arange 节点;违规时 ValueError(含改法提示)。
功能需求 6:VF Arange Codegen(PR 1961)
Arange 作为零输入向量生产节点融合进 VF,MicroAPI
Reg::Arange按块生成数值序列。VF 子图内 Arange 节点的 base/step/逻辑 strides/物理 vectorized 布局。
逻辑值偏移(
logical_offset + 块内偏移)× step 计算取值;物理 UB 地址按 vectorized strides 前进;outer-for 场景外层调度轴偏移并入逻辑 offset;多 Arange 各自参数独立。VFCall...函数中的Reg::Arange指令序列;支持最多 4 个 vectorized_axis,超过 4 轴在任何图改写前明确失败。功能需求 7:IndexExpr VF 值参数(PR 1961)
IndexExpr 值经 VF 函数标量参数传入消费算子,随调用点 tiling 变化,与 DS-7 的根图具名常量构成"VF 参数 / 根图变量"双形态。
VF 子图内消费 IndexExpr 的 Micro 调用(binary/compare scalar 变体)。
VF 签名收集 scalar 输入形参;调用点以根图 IndexExpr 变量名实参传入。
VFCall...(…, scalar_<name>, …)标量传参;不含 IndexExpr 的 VF 签名不变。功能需求 8:退化轴 Arange 广播透传(PR 1980)
显式"Arange→单输入 Broadcast"对,当 Arange 为多轴退化视图(恰好一根变化轴 stride=1,其余退化)且与 Broadcast 输出同轴数时,视图原样保留;Arange 为 1D(轴数更少)时按 Broadcast 输出位置补退化轴。
Arange 视图(axis/size/strides 三列表等长)、Broadcast 输出视图(尾轴一致、尾 stride=1)、双方均无控制边。
逐对校验(轴数 ≤、变化轴唯一且 stride=1、变化轴存在性、尾轴对齐、无控制边);不满足任一条件整个 pass 失败(fail-closed,不做无法证明安全的改写)。
Arange 视图(透传或补齐后)、
sched.axis与 Broadcast 对齐;违规时Normalize explicit Arange Broadcast views failed错误。功能需求 9:根图 Arange 物化(ArangeApiCall,PR 1980)
根图 Arange 由新 ApiCall 生成显式循环:
for(arange_i) SetValue(base + (logical_offset + arange_i) * step);非固定广播轴展开为内层arange_b帧循环。节点 ir_attr 的 base/step(SizeExpr);输出张量 vectorized 布局。
从最内轴向外扫描:退化轴跳过(对物理偏移与逻辑取值均无贡献);固定轴记 has_fixed_axis;广播轴入 broadcast_frames;变化轴做有界连续校验(size==轴尺寸且物理 stride==累积宽度),无法证明安全即报错。
device_impl.cpp 中的物化循环;烘焙广播视图先经 MaterializeArangeViewContiguous 连续化物理布局。
功能需求 10:VF 准入门控(PR 1980)
四类节点拒绝进 VF:①烘焙广播 Arange(位置无关检测:非退化零 stride);②带控制边 Arange;③UBFuse 上下文 Arange(graph attr 传递);④8↔4 字节 Cast(含 float↔int64)。
节点视图 strides/repeats、控制边计数、graph attr。
RefineEnableVFFlag建簇前判定;UBFuse 标记由 BufQueAllocator 写、PlatformV2 读(不扩展 BasePlatform 公共接口)。enable_vf = false,节点留在根图走对应 ApiCall(ArangeApiCall / CastExtend)。功能需求 11:VF 混合 dtype 掩码统一(PR 1980)
VF 所有嵌套循环的
UpdateMask/CreateMask统一使用子图最宽计算 dtype(loop_dtype),各张量 Load/Store 保持自身 dtype。子图各算子 dtype 集合。
GenerateFuncDefinition计算loop_dtype = max_dtype_size_,root_loop_.SetLoopDtype递归传播到全部嵌套循环。掩码位与计算寄存器 lane 对齐(修复每 4 个元素坏 1 个)。
功能需求 12:Store DMA 方向与常量 Cast 修复(PR 1980)
BuildDataCopyApiParamInNormal的 ub_tensor 实参由固定 dst 改为copy_in ? dst : src;常量 Cast 分支改用 Duplicate 直铺。Load(copy_in=true,src=GM/dst=UB)/ Store(copy_in=false,src=UB/dst=GM);常量输入的 Cast。
按 copy 方向显式选择 UB 侧实参;常量值经 GetScalarValue 内嵌生成代码。
非连续 Store 的 DMA 块参数正确;常量链少一个中间张量。
功能需求 13:绑定引用计数修复(PR 1980)
SizeExpr::Compare返回新引用并实现Py_NE/NotImplemented。Python 比较运算(
==/!=/<等)。richcompare 按 CPython 协议返回
Py_RETURN_TRUE/FALSE,IsConvType/IsCubeType同步修复。py module UT 在 Python ≤3.10 不再退出崩溃;不支持的比较返回干净 TypeError。
非功能需求
可维护性
mutable loop_dtype_已注释说明取舍;logical_strides收集三处同构(后续可提取 helper);可测试性
sh build.sh -u --module=autofuse_framework -j 8;tool/index_expr/run_index_expr_regression.sh(计划生成与 runner preflight 零漂移);可移植性
可靠性
特性交叉影响
已按 cross_feature_check.md 逐项检查:
性能
编译时长
执行性能
内存和产物大小
无 run 包布局变化。
接口设计
新增/修改接口描述
IndexExpr.expr: ExpressionArange.base/step: Expressionascir_api.arange(...)ascir_api.IndexExpr(...)optimize::NormalizeArangeBroadcastViews(AscGraph&)VectorFuncPartitioner(graph, disable_arange_vf=false)kGraphAttrIsUBFuseContext"ArangeApiCall"SizeExpr.__eq__/__ne__!=可用、<等干净 TypeError接口检查项
软件设计
关键数据结构
DS-1 双视图张量模型(全链路数据基座)
Arange 的一切判定都建立在逻辑 view 与物理布局分离的既有张量模型上
(
AscTensorAttr/codegen::Tensor,字段见codegen_kernel.h:97):axis/repeats(size) /stridesArangeHasBroadcastOuterAxis、Tiler::Offset(逻辑偏移)、前端校验vectorized_axis/vectorized_axis_pos/vectorized_stridesvectorized_axis_pos回指逻辑 view 下标Tiler::TensorVectorizedOffset(物理偏移)、GetArangeBlockOffset、ArangeApiCall扫描生命周期:随图节点输出 attr 由构图写入,normalize/scheduler 依调度结果重写
vectorized_*,codegen 只读;无独立所有权(附属于节点输出)。设计含义:取值语义永远由逻辑 strides 决定,物理摆放由 vectorized_strides
决定。透传、门控、双偏移三个算法都只做"从两组字段各自读取正确一侧",
不改写彼此,这是图改写等价性可证明的结构基础。
DS-2
ArangeParam——VF 内 Arange 的参数载体(1980 扩展)struct ArangeParam { // vf_loop.h:19 ascir::TensorId tensor_id; // 关联 Arange 输出张量(参数命名后缀) std::string base; // base 表达式串 std::string step; // step 表达式串 std::string offset; // 外层调度逻辑偏移(outer-for 场景) std::vector<std::string> logical_strides; // 1980 新增:各逻辑轴 stride 形参名 };由
VFLoop::CollectArangeParams递归收集(值语义,codegen 期临时对象),logical_strides从tensor->axis_strides_逐轴填充。直接决定 VF 函数签名:// vec_func_call.cpp:506 生成的形参(每个 VF 内 Arange 一组) int64_t arange_offset_<id>, int64_t arange_base_<id>, int64_t arange_step_<id>, int64_t arange_stride_<id>_0, ..., int64_t arange_stride_<id>_<n-1>多 Arange 隔离:参数以
tensor_id为命名空间,同一 VF 内 N 个 Arange互不串扰。签名兼容性:不含 Arange 的 VF 签名不变(1961 确立的约束)。
DS-3
VFLoop循环树——VF 代码的结构载体(1980 修复)struct VFLoopBody { LoopType type_; // LOOP 或 CALL union { MicroApiCall *call_; VFLoop *loop_; }; }; class VFLoop { std::vector<VFLoopBody> bodys_; // 嵌套循环/调用体 std::string max_dtype_size_; // 子图最宽计算 dtype(构造期计算) mutable std::string loop_dtype_; // 1980 新增:掩码 dtype(递归传播) };ConstructFromNodes自 VF 子图构建(new 出的子循环由Destruct()递归释放),随VfCall代码生成期存活。max_dtype_size_在遍历子图全部输出时按GetSizeByDataType取最大字节宽确定(vec_func_call.cpp:342),随后SetMaxDtypeSize→SetLoopDtype递归传播给整棵循环树。mutable的取舍:Generate为 const 接口,传播发生在生成前一刻;已注释说明。DS-4
CallParam——MicroAPI 调用上下文(1980 扩展)struct ArangeParams { bool valid; std::string base; std::string step; }; struct CallParam { std::string p_reg; // 掩码寄存器名 std::string offset; // 块偏移(Arange 场景) std::string max_dtype_size; // 调用侧 dtype ArangeParams arange; }; // Arange 参数直通VFLoop::GenerateBody对MicroApiName()=="Arange"的调用先算块偏移再经param.arange把 VF 形参名(arange_base_<id>/arange_step_<id>)传入MicroArangeApiCall::Generate,与根图直连场景(用节点自身表达式)区分。DS-5
ArangeApiCall——根图 Arange 出码器(1980 新增)class ArangeApiCall final : public ApiCall { af::Expression base_; // ParseAttr 从 ir_attr 解析 af::Expression step_; // Generate 为纯函数:扫描状态(slice/broadcast_frames/has_fixed_axis) // 均为栈上局部量,无跨调用状态 };工厂经
static ApiCallRegister<ArangeApiCall> register_arange_api_call("ArangeApiCall")注册;注册名
"ArangeApiCall"由 v2 注册表GetApiCallName()提供(1980 之前为空串导致根图 Arange 无法出码)。持有零输入张量状态。
DS-6
kGraphAttrIsUBFuseContext——上下文标记(1980 新增)图级 attr 键:BufQueAllocator 在 UBFuse 分配时写入,PlatformV2 分区时读取。
选择 graph attr 而非 BasePlatform 公共接口,避免为单一消费方扩公共 ABI;
生命周期与图一致,一次写一次读。
DS-7 IndexExpr Expression 常量——符号标量的双形态载体(1961 建立、1980 扩展)
IndexExpr 不是普通 UB tensor,值在 codegen 中以两种形态存在:
Tensor::const_value_expr(ascir::SizeExpr,codegen_kernel.h:127)经TPipe::AddTensor(output, expr, name)注册ParseGraphIndexExpr 分支 +BlkTensorAllocAndInit(1980 补ParseOptimizeInfo与符号回退)GetScalarValue():const_value为空时返回生成后的具名 tensor 变量名(codegen_kernel.h:164)VfCall签名中的标量形参CollectArangeParams同源的 scalar 输入收集关键不变量:值为 0 的 Expression 仍是有效表达式(1961 修复"零表达式被误判
为空值");符号值 Scalar 的
const_value字符串为空属正常态,必须回退tiler.Size(const_value_expr, true)而非产出空串(1980 修复非法 C++)。DS-8
IsScalarNodeType——scalar-like 白名单(1961 确立)bool IsScalarNodeType(const std::string &node_type) { // micro_binary_scalar_api_call.cpp:17 return node_type == Scalar::Type || node_type == ScalarData::Type || node_type == IndexExpr::Type; }IndexExpr 与 Scalar/ScalarData 同类调度与同路识别,但三者行为不完全等价
(如 ScalarData 的常量直取路径与 IndexExpr 的符号解析路径不同);白名单
同时被 partition(直连准入)与 codegen(
s后缀变体选择、参数交换)消费,是消费边协议(AL-9)的判定原语。
关键技术/算法
AL-1 退化轴透传等价性(1980,透传正确性证明)
命题:设 Arange 逻辑 view 第 i 轴退化(
repeats[i]=1, strides[i]=0),则从 view 中删除或补入该轴,物化结果与消费语义均不变。
证明分两支(对应 DS-1 双视图):
TensorVectorizedOffset输出的写地址表达式不变;Tiler::Offset中stride==0直接输出常数
"0"(codegen_kernel.cpp:665)——删除后取值表达式不变。因此扩维(值沿该轴重复)可以完整移交 Broadcast 节点的通用
BroadcastExtend路径处理,Arange 只物化扁平一维序列——与已验证的退化轴
Load[4,1] → Broadcast[4,8]路径同构。改动前实现把 Broadcast输出 repeats 烘焙进 Arange view(
repeats=[4,8]),与"保持 Broadcast扩维职责"的注释自相矛盾,且 Pattern B 物化代价 O(rows×cols) 标量写;
透传后降为 O(单轴) 标量写 + 向量化 BroadcastExtend,严格占优。
AL-2 1D 补齐算法(1980,normalize 内)
对"Arange 轴数 < Broadcast 输出轴数"的 1D 形态(1961 前端协议),按
Broadcast 输出轴序逐位置补齐:变化轴位置保留自身 size 与 stride=1,
其余位置补
(size 1, stride 0);vectorized_strides按补齐后 repeats重建为扁平连续布局(从尾到头累积
flat_stride *= axis_size)。前置校验(fail-closed,任一不满足整体 Optimize 失败):
IsDegenerateAxis判定);同轴数多轴退化视图直接透传(仅同步
sched.axis)。复杂度:单次线性扫描 Broadcast 节点 × 视图长度,无嵌套。
AL-3
ArangeApiCall尾到头扫描状态机(1980,根图物化核心)对
output.vectorized_axis从最内轴向外逐轴分类(四个状态):axis_size[pos]==1 && axis_strides[pos]==0continue跳过(对两偏移均无贡献,见 AL-1)current_axis(外层循环已固定该轴前缀)has_fixed_axis=true;count 改用切片实际尺寸axis_strides[pos]==0且非退化broadcast_frames(帧尺寸, 物理 stride),物化时展开帧循环axis_size[pos]==axis.size(值域有界)且vectorized_strides[index]==累积 stride(物理连续);加入连续切片slice伴随的守卫:
IsFrom(current, id)禁止未证实的派生轴局部范围(防止tiling 分裂后 current_axis 与逻辑 view 的对应关系无法证明);
!GetAxis(slice.front()).IsInner()禁止多轴内尾压平。有界连续校验失败即
FAILED——不做无法证明安全的猜测性出码,由上层
Optimize fail携带原因退出。这是根图路径的 fail-closed 边界。AL-4 烘焙广播物化——双偏移分离(1980)
C++ 直连 ASCIR(无前端协议约束)仍可能携带烘焙广播 view(如
size=[4,8], strides=[1,0])。物化分两步:MaterializeArangeViewContiguous):把vectorized_strides按 repeats 从尾到头重算为连续布局(每轴 stride=右侧各轴尺寸之积),
物理侧恢复可连续写入;
for (arange_b0 < frame_size) { // 广播帧(物理 stride 前进) for (arange_i < count) // 连续序列 SetValue(write_addr + arange_i, // 物理偏移:连续/帧步长 base + (logical_offset + arange_i) * step); // 逻辑偏移:stride 语义 }write_addr(TensorVectorizedOffset+ 帧偏移)决定写在哪里;logical_offset(Tiler::Offset,逻辑 stride 求值)决定取什么值。两者解耦是烘焙广播可物化的充分条件——值重复(逻辑 stride=0)与
物理前进(连续布局)互不干扰。
AL-5 VF Arange 块偏移与 outer-for 合成(1961 确立、1980 参数化)
VF 内 Arange 每块取值 =
base + (外部逻辑偏移 + 块内偏移) × step:GetArangeBlockOffset,vf_loop.cpp:84):找 lane 轴(vectorized_strides 尾部最后一个非零轴),lane 轴贡献
ELEMENT_PER_VECTOR_LENGTH(向量寄存器一次装载的元素数),外层轴贡献
axis_var * logical_stride;逻辑偏移经 VF 形参
arange_offset_<id>传入,与块内偏移相加;全量逻辑 strides 亦形参化(
arange_stride_<id>_<k>),使同一 VF函数可被不同 tiling 的调用点复用;
MicroArangeApiCall::Generate):block_base = vf_base + block_offset × vf_step,随后按 step 静态值分派:
step==1→ 单条Reg::Arange(block_base);step≠1/动态→Reg::Arange(0)+Reg::Muls(step, p_reg)+Reg::Adds(block_base, p_reg)三指令降级(掩码寄存器
p_reg保证尾块按掩码长度计算)。正确性来源:
Reg::Arange只能表达单条连续等差序列——这正是 AL-6把烘焙广播挡在 VF 外的根本原因(MicroAPI 能力边界即算法边界)。
AL-6 位置无关广播检测(1980,门控核心)
ArangeHasBroadcastOuterAxis:遍历逻辑 strides,任一轴满足stride==0 && repeats!=1即判定为烘焙广播(值沿该轴重复),拒绝进 VF。位置无关的含义:旧实现只检查"lane 轴之前"的零 stride(Pattern A 假设),
Pattern B(
strides=[1,0],广播在尾轴)漏检。新规则与 normalize(AL-2 的 IsDegenerateAxis)、前端校验(构图期拒绝)三处共用同一语义
——"非退化 + 零 stride = 广播",不依赖轴位置,三处判定零漂移。
AL-7 掩码 dtype 统一传播(1980,修复混合 dtype 错位)
缺陷机理:
UpdateMask<T>(len)按 dtype T 的每向量元素数生成掩码;循环与计算 dtype 不一致时(如 int64 掩码配 float32 计算),掩码位与
计算寄存器 lane 错位——每
sizeof(宽)/sizeof(窄)个元素损坏 1 个。修复算法:
ConstructFromNodes遍历子图全部输出取最宽 dtype 为max_dtype_size_;SetLoopDtype自根循环递归传播到全部嵌套循环;所有
UpdateMask统一用loop_dtype_(vf_loop.cpp:435),各张量的Load/Store 仍用自身 dtype。不变量:掩码跟随最宽计算 dtype,张量各自
保持 dtype——掩码是 lane 对齐的全局资源,取值是张量局部行为。
AL-8 逻辑/物理偏移原语(1961 基座,两路径共用)
// codegen_kernel.cpp:640 —— 逻辑偏移:stride 0→"0",1→axis,否则 axis*stride Tiler::Offset(current_axis, axis, axis_strides); // codegen_kernel.cpp:682 —— 物理偏移:仅累计 current_axis ∩ vectorized_axis Tiler::TensorVectorizedOffset(current_axis, tensor);VF 路径(AL-5 的块偏移合成)与根图路径(AL-3/AL-4 的双偏移)消费同一
对原语,保证两条 codegen 路径对同一视图的偏移语义一致——这是双路径
互补(VF 不变 + 根图回退)在算法层的落点。
AL-9 IndexExpr 消费边分类协议(1961 确立)
逐边协议表(partition 直连准入 + codegen 变体选择):
s后缀(Add→Adds)Init断言)IsNodeValid不支持 scalar 输入重要边界:
InsertBroadcastAfterScalarForAscGraph仅收集 Scalar、不收集IndexExpr(
scalar_broadcast_insert.cpp:124-139),VF 的 Broadcast 消除也只把根源 Scalar 作为候选(
scalar_broadcast_optimization.cpp:45-61)——因此"optimizer 自动为 IndexExpr 插入 Broadcast"不构成当前契约,前端必须显式
构图 Broadcast(
scalar_consumer/composition/compare_mask/window_math均如此)。
AL-10 IndexExpr 浮点消费改写等价性(前端契约)
改写:
index_expr(expr, f32)→IndexExpr(expr, int64) → Broadcast → Cast(f32)。等价性论证:
expr值域为整数,int64 物化无截断(inductor size/index 语义按 int64 管理);int64 → f32舍入与 inductor 参考实现一致:参考实现同样把整数值转 f32参与运算,numel > 2²⁴ 时双方一致舍入,对齐参考即正确;
形态约束:Cast 必须在 Broadcast 之后——IndexExpr 输出无 axis/repeats,
标量域无 Cast 的合法形态;顺序颠倒会因 Cast 无 view 失败。guard 分支:
dtype 依赖运行时信息无法静态确定时走 fallback(不融合),与 eager fallback
一致,不产生半改写图。
AL-11 IndexExpr 标量块物化与符号回退(1980)
根图 IndexExpr 与 Scalar 共享 blk tensor(32B 对齐标量块)物化链,三个环节:
ParseScalarNeedGenBlkTensors的类型过滤器从Scalar | ScalarData扩为Scalar | ScalarData | IndexExpr——决定哪些节点生成 blk tensor;
BlkTensorAllocAndInit中const_value.empty() ? tiler.Size(const_value_expr, true) : const_value——符号值 Scalar/IndexExpr 的字符串常量为空,直接用会生成空串非法 C++,
回退用
tiler.Size把符号表达式转成 tiling 参数引用;ParseGraph的 IndexExpr 分支补ParseOptimizeInfo,与 Scalar 分支对齐——此前只
AddTensor不解析优化信息,后续 tiling查不到该张量。
三处缺一都会导致索引消费链拿到未初始化/未对齐/查不到的标量块。
流程设计
主流程:构图(校验)→ GraphPass(normalize,合轴前)→ 分区(门控)→
调度(对齐策略)→ codegen(VF 或根图)。异常流程:任一校验失败即整体
Optimize 失败并携带原因;无部分改写状态(视图改写原子完成)。
对子模块的修改
test_index_expr_arange_schema.cpp、micro_arange_api_callUT)。逐文件说明见pr_1961_change_summary.md。pr_1980_code_change_rationale.md第 4 章。错误处理
系统错误
接口错误
Optimize fail携带 pass 名(如Normalize explicit Arange Broadcast views failed);__eq__正常返回 bool;不支持的比较返回 NotImplemented(Python 层表现为 TypeError)。安全检查
已读取并检查 编码红线.md:
pos < axis_size.size()等)✔兼容性检查
ascir_api.arange拒绝非退化零 stride。影响面:直接构图脚本需改用 1D Arange + 显式 Broadcast 或退化视图;
TorchInductor 前端按新协议生成,不受影响。
测试设计
测试边界
入口:py module UT(构图+schedule+codegen 断言)、v35 UT(分区/codegen
文本断言)、真机 device validation(codegen→JIT→编译→load→launch→
精度)。打桩:ARANGE_GRAPH_PROBE 控制边注入。验证数据:numpy 随机输入
composition、compare_mask、window_math(+规划 float_indexexpr_cast_mul);
Arange 覆盖用例:arange_store、degenerate_arange_add(_3d)、triu_arange_mask、
broadcast_add、auto_arange_fallback 等。
测试用例设计
IndexExpr(int64)→Broadcast→Cast(f32)(FR4 规划)[32,64]/[32,65]/[2,127])+ pytest 构图正向用例Check dtype failed)!=/<协议(1980)测试命令
sh build.sh -u --module=autofuse_framework -j 8 # 预期: 全绿 (py 267+ passed) bash tool/index_expr/run_index_expr_regression.sh # 预期: 55 组合 PASS=55 FAIL=0验收标准
21,329,846 元素 mismatch=0、nan=0;triu [128,408,408] 掩码链
2130 万元素零误差(max_abs_error=0.0);3D 退化轴组合索引通过。
ci-pipeline-passedlabel 生效。设计文档检查结果