fix(mlir): GEP 常量索引写字面量并修正类型列表

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 索引例外说明。
This commit is contained in:
TermiNexus committed 2026-10-03 21:33:25 +08:00
1 parent bff0f20f51
commit aa94d67183
2 files changed
+35 -22

No files matched your search

+6 -2
View File
@@ -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 → 块参数
+29 -20
View File
@@ -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):