From aa94d67183671ef908de85bf72d756f396cdb04e Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 3 Oct 2026 21:33:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(mlir):=20GEP=20=E5=B8=B8=E9=87=8F=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E5=86=99=E5=AD=97=E9=9D=A2=E9=87=8F=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E7=B1=BB=E5=9E=8B=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mlir-opt 的 llvm.getelementptr 要求“索引到结构体”的索引必须是常量字面量, 而 _MlirGep 原先把常量索引经 _MlirOperand 物化成 SSA 操作数(%cN), 触发 verifier: error: 'llvm.getelementptr' op expected index 1 indexing a struct to be constant 按 MLIR LLVMDialect 的定义修正为官方形态: - 常量索引 -> 整数字面量([0, 1]),不物化、不占类型位; - 仅 SSA 索引物化为操作数,其类型进入 (...) 类型列表。 同步修正 README §2.1 映射表(原 [%c0, %c1] : (!llvm.ptr, i32, i32) 形态错误), 并在 §2.3 补充“常量必须物化”的 GEP 索引例外说明。 --- mlirlite/README.md | 8 +++++-- mlirlite/__function.vp | 49 +++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/mlirlite/README.md b/mlirlite/README.md index f6fa298..4a5b8e8 100644 --- a/mlirlite/README.md +++ b/mlirlite/README.md @@ -125,8 +125,8 @@ llvm.func @add(%a: i32, %b: i32) -> i32 { | `%N = load i32, !llvm.ptr %p` | `%N = llvm.load %p : !llvm.ptr -> i32` | | `store i32 %v, !llvm.ptr %p` | `llvm.store %v, %p : i32, !llvm.ptr` | | `%N = getelementptr i32, !llvm.ptr %p, i64 %i` | `%N = llvm.getelementptr %p[%i] : (!llvm.ptr, i64) -> !llvm.ptr, i32` | -| `%N = getelementptr !llvm.array<4 x i32>, !llvm.ptr %p, i32 0, i64 %i` | `%N = llvm.getelementptr %p[%c0, %i] : (!llvm.ptr, i32, i64) -> !llvm.ptr, !llvm.array<4 x i32>` | -| `%N = getelementptr !llvm.struct<(i32, !llvm.ptr)>, !llvm.ptr %p, i32 0, i32 1` | `%N = llvm.getelementptr %p[%c0, %c1] : (!llvm.ptr, i32, i32) -> !llvm.ptr, !llvm.struct<(i32, !llvm.ptr)>` | +| `%N = getelementptr !llvm.array<4 x i32>, !llvm.ptr %p, i32 0, i64 %i` | `%N = llvm.getelementptr %p[0, %i] : (!llvm.ptr, i64) -> !llvm.ptr, !llvm.array<4 x i32>` | +| `%N = getelementptr !llvm.struct<(i32, !llvm.ptr)>, !llvm.ptr %p, i32 0, i32 1` | `%N = llvm.getelementptr %p[0, 1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, !llvm.ptr)>` | | `%N = bitcast !llvm.ptr %p to !llvm.ptr` | **no-op**:不输出指令,`%N` 登记为 `%p` 的别名(后续引用自动改写) | | `%N = bitcast i32 %v to f32` | `%N = llvm.bitcast %v : i32 to f32` | | `%N = trunc i64 %v to i32` | `%N = llvm.trunc %v : i64 to i32`(zext/sext/ptrtoint/inttoptr/fpext/fptrunc/fptosi/sitofp/fptoui/uitofp 同构) | @@ -166,6 +166,10 @@ MLIR 的操作数不能是字面量。**扫描阶段**(pass 1)遍历函数 - 常量统一放 entry 块开头 ⇒ entry 支配全函数,常量在所有块内可见, 且**不破坏 phi 的"块首"约束**(phi 只在非 entry 块,块首仍由 phi 独占); - `phi` 入边里的常量同样会被物化(`phi` 行在 pass 1 也参与扫描,只是发射阶段不输出)。 +- **例外 —— `getelementptr` 的索引不是"操作数"而是索引属性**:常量索引必须写成 + **整数字面量**(`[0, 1]`),**不**物化为 `%cN`(否则 MLIR verifier 报 + `expected index N indexing a struct to be constant`);只有 SSA 索引才物化并 + 占类型位,故 `(…)` 类型列表只列 SSA 索引的类型(见 §2.1 表)。 ### 2.4 phi → 块参数 diff --git a/mlirlite/__function.vp b/mlirlite/__function.vp index e1abe9a..ffb1596 100644 --- a/mlirlite/__function.vp +++ b/mlirlite/__function.vp @@ -6,9 +6,9 @@ import memhub import stdio import stdlib from linkedlist import GSListNode, GSList -from .__types import ( - LLVMType, ParamNode, TypePrint, - MlirTypeText, MlirTypeTextIsPointer, MlirFail, MlirSetContextLine, +from .__types import ( + LLVMType, ParamNode, TypePrint, + MlirTypeText, MlirTypeTextIsPointer, MlirFail, MlirSetContextLine, ) @@ -20,11 +20,11 @@ IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000 # 窥孔优化总开关(T2 转发/ret 归零、T3 no-op bitcast 消除)。 # TPV 命令行 --no-llvmpeephole 会将其置 0:FunctionPrint 序列化时跳过全部本地窥孔, # 便于与关闭窥孔的 IR 对比调试(RenumberSSA 仍保留,保证输出 SSA 编号合法)。 -G_PeepEnable: bool = True - -# 函数文本输出截断标志:_append_cstr 写入被输出缓冲上限截断时置 1, -# FunctionPrint 结束前检查并 MlirFail —— 截断的 MLIR 会让 mlir-opt 报难以定位的 -# 语法错(且此前是**静默**丢内容),必须响亮失败。 +G_PeepEnable: bool = True + +# 函数文本输出截断标志:_append_cstr 写入被输出缓冲上限截断时置 1, +# FunctionPrint 结束前检查并 MlirFail —— 截断的 MLIR 会让 mlir-opt 报难以定位的 +# 语法错(且此前是**静默**丢内容),必须响亮失败。 _MlirOutTrunc: int = 0 @@ -92,7 +92,7 @@ novirtual class Function(GSListNode[Function]): Attrs: str # 函数属性文本(None=无属性) AttrGroup: str # 字符串属性组内容(如 '"section"=".text.startup"',None=无) AttrGroupId: int # 属性组编号(>0 时函数行输出 #N,模块尾输出 attributes #N) - NameLen: sizet # 函数名长度(创建时一次算好,恒等于 strlen(Name);追加在末尾,既有字段偏移不变) + NameLen: sizet # 函数名长度(创建时一次算好,恒等于 strlen(Name);追加在末尾,既有字段偏移不变) DbgRef: str # DISubprogram ref(同形保留;MLIR 打印阶段忽略,如 "!3") @@ -2640,16 +2640,25 @@ def _MlirGep(p: str, resname: str, out: str, outsz: sizet): p = _mlir_ws(p) ivb: str = _SC_V1 p = _mlir_read_value(p, ivb, 512) + # 常量索引必须写成**整数字面量**(如 `[0, 1]`),不能物化成 SSA 操作数: + # MLIR 的 llvm.getelementptr 要求“索引到结构体”的索引是常量,否则 + # verifier 报 'expected index N indexing a struct to be constant' + # (操作数形式只会进 rawConstantIndices 的动态占位)。仅非常量(SSA + # 值,以 '%' 开头)索引才物化为操作数并占类型位;常量索引不占类型位。 if nidx > 0: _append_cstr(idxv, 1024, ", ") - _append_cstr(idxt, 1024, ", ") - _MlirOperand(ivb, ityb, tmpv, 4096) - _append_cstr(idxv, 1024, tmpv) - tmpv[0] = '\0' - imt: str = _SC_Mt - imt[0] = '\0' - MlirTypeText(imt, 512, ityb, _MLIR_Pool) - _append_cstr(idxt, 1024, imt) + if ivb[0] == '%': + _MlirOperand(ivb, ityb, tmpv, 4096) + _append_cstr(idxv, 1024, tmpv) + tmpv[0] = '\0' + imt: str = _SC_Mt + imt[0] = '\0' + MlirTypeText(imt, 512, ityb, _MLIR_Pool) + if idxt[0] != '\0': + _append_cstr(idxt, 1024, ", ") + _append_cstr(idxt, 1024, imt) + else: + _append_cstr(idxv, 1024, ivb) nidx += 1 p = _mlir_ws(p) if nidx == 0: @@ -3226,14 +3235,14 @@ def FunctionPrint(buf: str, size: sizet, # declare(无函数体) _MlirFuncSig(buf, size, func, pool) _append_cstr(buf, size, "\n") - _MlirOutTruncFail() + _MlirOutTruncFail() return blocks: rawptr[GSList[BasicBlock]] = func.Blocks if blocks is None: _MlirFuncSig(buf, size, func, pool) _append_cstr(buf, size, "\n") - _MlirOutTruncFail() + _MlirOutTruncFail() return # ---- pass1:扫描(登记常量/别名 + 形态校验)---- @@ -3295,7 +3304,7 @@ _g_last_pos: sizet = 0 def _append_cstr(dst: str, dst_size: sizet, src: str): """将 C 字符串 src 追加到 dst 末尾""" - global _g_last_dst, _g_last_pos + global _g_last_dst, _g_last_pos, _MlirOutTrunc if dst is None or src is None: return dlen: sizet = 0 if _g_last_dst == t.CUInt64T(dst):