fix(memhub,gzip): 内存池 OOM 归属画像与 free 拒绝现场;gzip 注解 *= 词法粘连
memhub: alloc 失败打印归属 site/ctx 与存活块 order 分布(新增 dump_alive_orders);free 被 _is_valid_ptr 拒绝时首次打印现场;接通 big_alloc_count gzip: ub: uchar*= buf 补空格(*= 被词法合并为单 OP,注解止于 uchar) 新增 traceback 模块;删除 dbgstack.vp;其余模块随新语法与尺寸口径同步
This commit is contained in:
1 parent
093ff5f66d
commit
db3be6968f
32 files changed
+6870
-3058
No files matched your search
@@ -40,7 +40,10 @@ class list[T]:
|
||||
self.__elem_size__ = T.__sizeof__()
|
||||
else:
|
||||
self.__elem_size__ = elem_size
|
||||
self.__data__ = pool.alloc(self.__capacity__ * self.__elem_size__)
|
||||
# 带申请点 tag:本处是"容量 × 元素大小"形态,尺寸为 0 时 [MEM] 报文直接
|
||||
# 指名本行(pool 是基类指针,故走 memhub.MemAllocTagged)
|
||||
self.__data__ = memhub.MemAllocTagged(pool, self.__capacity__ * self.__elem_size__,
|
||||
memhub.DETSITE_LIST_43)
|
||||
self.__iter_index__ = 0
|
||||
|
||||
def __len__(self) -> sizet:
|
||||
|
||||
+6
-7
@@ -12,7 +12,9 @@ define _MAX_PROVIDER_DEPTH = 32
|
||||
|
||||
class _ProviderStack:
|
||||
"""Provider stack, stores pairs of field names and pointers."""
|
||||
names: [32]char
|
||||
# Field names are stored as string pointers: a `char` element would hold a
|
||||
# single byte, truncating the name and breaking every provider lookup.
|
||||
names: [32]str
|
||||
ptrs: [32]rawptr
|
||||
top: uint64
|
||||
|
||||
@@ -71,10 +73,7 @@ def _find_provider(field_name: str) -> rawptr:
|
||||
# __mbuddy__ 注入全部失效,_str.upper 对 NULL 取 vtable 崩溃)。显式转换让
|
||||
# 符号域与递减语义一致,与编译器侧「stop 为负字面量时退回有符号谓词」互为双保险。
|
||||
for i in range(int(stack.top) - 1, -1, -1):
|
||||
# 字段名用 strcmp 内容比较:编译器对每次 create_global_string 生成
|
||||
# 独立的字符串常量(TPV 不去重),push/find 的指针地址不同,
|
||||
# 指针 == 永假会导致 provider 匹配失败(__mbuddy__ 注入不上)。
|
||||
_cmp_tmp: [2]char = [0, 0]
|
||||
_cmp_tmp = stack.names[i]
|
||||
if string.strcmp(_cmp_tmp, field_name) == 0: return stack.ptrs[i]
|
||||
# Content comparison: every string literal gets its own constant, so the
|
||||
# push/find pointers differ and pointer equality never matches.
|
||||
if string.strcmp(stack.names[i], field_name) == 0: return stack.ptrs[i]
|
||||
return None
|
||||
+8
-8
@@ -29,18 +29,18 @@ import hashtable
|
||||
# ============================================================
|
||||
|
||||
# Type constants
|
||||
STRING: t.CDefine = 0
|
||||
INT: t.CDefine = 1
|
||||
BOOL: t.CDefine = 2
|
||||
define STRING = 0
|
||||
define INT = 1
|
||||
define BOOL = 2
|
||||
|
||||
# Action constants
|
||||
STORE: t.CDefine = 0
|
||||
STORE_TRUE: t.CDefine = 1
|
||||
STORE_FALSE: t.CDefine = 2
|
||||
COUNT: t.CDefine = 3
|
||||
define STORE = 0
|
||||
define STORE_TRUE = 1
|
||||
define STORE_FALSE = 2
|
||||
define COUNT = 3
|
||||
|
||||
# Limits
|
||||
MAX_ARGS: t.CDefine = 32
|
||||
define MAX_ARGS = 32
|
||||
|
||||
# Module-level membuddy pointer (assigned inside main by user)
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
+96
-50
@@ -1189,6 +1189,14 @@ def _parse_arith(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
# ============================================================
|
||||
# C 风格指针后缀('*' / '**')—— 与 TransPyC lib/vast 的 _AtPtrSuffix 同源
|
||||
# ============================================================
|
||||
# Start position (line/col) of the last consumed C-style pointer suffix ('*' / '**').
|
||||
# _parse_paren matches it against ps.prev to tell the paren-less cast `(T*)x` from a
|
||||
# plain group like `(a | b)`. Recorded as ints, not as a token pointer: comparing two
|
||||
# token pointers whose declared pointer levels differ is rejected by the compare check.
|
||||
_g_ptr_suffix_line: int = 0
|
||||
_g_ptr_suffix_col: int = 0
|
||||
|
||||
|
||||
def _ptr_suffix_end_op(op: int) -> int:
|
||||
"""后缀末尾 token 是否为“无右操作数”位置(']' ',' ')' ':' '=')。
|
||||
|
||||
@@ -1240,21 +1248,45 @@ def _make_ptr_type(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]:
|
||||
|
||||
def _consume_ptr_suffix(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]:
|
||||
"""消费连续的 C 风格指针后缀('*' 一层 / '**' 两层),展开为 base | t.CPtr 链。"""
|
||||
global _g_ptr_suffix_line, _g_ptr_suffix_col
|
||||
while True:
|
||||
_skip_nl(ps)
|
||||
if _match_op(ps, TokOp.StarStar):
|
||||
_advance(ps)
|
||||
_g_ptr_suffix_line = ps.prev.lineno
|
||||
_g_ptr_suffix_col = ps.prev.col_offset
|
||||
base = _make_ptr_type(ps, base)
|
||||
base = _make_ptr_type(ps, base)
|
||||
continue
|
||||
if _match_op(ps, TokOp.Star):
|
||||
_advance(ps)
|
||||
_g_ptr_suffix_line = ps.prev.lineno
|
||||
_g_ptr_suffix_col = ps.prev.col_offset
|
||||
base = _make_ptr_type(ps, base)
|
||||
continue
|
||||
break
|
||||
return base
|
||||
|
||||
|
||||
def _at_cast_operand(ps: rawptr[Parser]) -> int:
|
||||
"""Whether the current token may start the operand of a paren-less cast `(T*)x`.
|
||||
|
||||
'(' is deliberately absent: `(T*)(x)` is already handled by the trailer loop as a
|
||||
cast call, and keeping it there leaves that path's AST untouched. Nl is not
|
||||
skipped either: a line break after ')' starts a new statement, not an operand.
|
||||
"""
|
||||
if _cur_type(ps) == TokenType.Name:
|
||||
return 1
|
||||
if _cur_type(ps) == TokenType.Number:
|
||||
return 1
|
||||
if _cur_type(ps) == TokenType.String:
|
||||
return 1
|
||||
op: int = _cur_op(ps)
|
||||
if op == TokOp.Lsqb or op == TokOp.LBrace or op == TokOp.Star or op == TokOp.Amp or op == TokOp.Minus or op == TokOp.Plus or op == TokOp.Tilde:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _at_ptr_suffix_call(ps: rawptr[Parser], base: rawptr[AST]) -> int:
|
||||
"""判定当前 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用**。
|
||||
|
||||
@@ -1666,11 +1698,25 @@ def _parse_paren(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
node = Tuple(ps.pool, elts, ASTCtx.Load)
|
||||
_set_pos(node, lineno, col, 0, 0)
|
||||
return node
|
||||
# Paren-less C cast `(T*)x`: the inner expression ends with a pointer suffix
|
||||
# (ps.prev is that suffix's '*'/'**' token) and the operand follows ')' directly.
|
||||
# Same product as `(T*)(x)` / `T*(x)`: a Call whose func is `T | t.CPtr`.
|
||||
is_ptr_cast: int = 0
|
||||
if _g_ptr_suffix_line > 0 and _match_op(ps, TokOp.RPar) and ps.prev.lineno == _g_ptr_suffix_line and ps.prev.col_offset == _g_ptr_suffix_col:
|
||||
is_ptr_cast = 1
|
||||
_skip_nl(ps) # (expr\n) 单表达式括号跨行收尾
|
||||
_expect_op(ps, TokOp.RPar)
|
||||
# Check for generator expression
|
||||
if _cur_kw(ps) == Keyword.For:
|
||||
return _parse_comprehension(ps, first, ASTKind.GeneratorExp, lineno, col)
|
||||
if is_ptr_cast != 0 and _at_cast_operand(ps) != 0:
|
||||
operand: rawptr[AST] = _parse_factor(ps)
|
||||
cast_args: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
cast_args.append(operand)
|
||||
cast_kws: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
cast_node: rawptr[AST] = Call(ps.pool, first, cast_args, cast_kws)
|
||||
_set_pos(cast_node, first.lineno, first.col_offset, 0, 0)
|
||||
return cast_node
|
||||
return first
|
||||
|
||||
|
||||
@@ -2165,56 +2211,56 @@ def _parse_varargslist(ps: rawptr[Parser], is_lambda: int = 0) -> rawptr[AST]:
|
||||
return Arguments(ps.pool, args, vararg, kwarg, defaults, kw_defaults)
|
||||
|
||||
|
||||
def _parse_array_prefix_declarator(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""C 风格前置数组声明符 `[N]T` / `[]T` → `t.CArray[T, N]` / `t.CArray[T, None]`
|
||||
|
||||
只在注解位置(_parse_type_ann)消费;当前 token 不是 '[' 时返回 None。
|
||||
脱糖目标与 TPC / StubGen / DeclarationGenerator 的规范数组形态同形
|
||||
(`t.CArray[E, N]`;无长度按 `t.CArray[E, None]` = 指针模式),即把
|
||||
`coeffs: const [8]double` 就地等价成文档给出的修法
|
||||
`coeffs: const t.CArray[double, 8]`(Debugger/type-descriptors.md §3 第 6 例)。
|
||||
|
||||
必须在 `_parse_test` 之前调用:否则 `[N]` 会被解析成 list 字面量,随后在元素
|
||||
类型标识符处报"意外的 token"(includes/binascii.vp `HEX_VALS: [256]int` 现象)。
|
||||
"""
|
||||
if _match_op(ps, TokOp.Lsqb) == 0:
|
||||
return None
|
||||
lb_lineno: int = _cur_lineno(ps)
|
||||
lb_col: int = _cur_col(ps)
|
||||
_advance(ps) # 消费 '['
|
||||
count_node: rawptr[AST] = None
|
||||
if _match_op(ps, TokOp.Rsqb) == 0:
|
||||
# 长度表达式(常量/常量表达式;非法长度由下游 ArrayDims 判据拒绝)
|
||||
count_node = _parse_test(ps)
|
||||
if count_node is None:
|
||||
_record_parse_err(ps)
|
||||
return None
|
||||
if _match_op(ps, TokOp.Rsqb) == 0:
|
||||
_record_parse_err(ps) # 缺少 ']'
|
||||
ps.error_count += 1
|
||||
return None
|
||||
_advance(ps) # 消费 ']'
|
||||
# 元素类型:递归 _parse_type_ann,故 `[N]const int` / `[N]int*` 走同一规则源
|
||||
elem: rawptr[AST] = _parse_type_ann(ps)
|
||||
if elem is None:
|
||||
return None
|
||||
if count_node is None:
|
||||
count_node = _new_const_none(ps.pool, lb_lineno, lb_col)
|
||||
# 元素大小必须显式传 8(list[T](pool, elem_size) 按字节步长寻址;
|
||||
# 传 2 会让 8 字节指针按 2 字节步长写入 → 堆越界,ASan 在 _set_parent_list
|
||||
# 抛 access-violation)。与文件内其它 list[rawptr[AST]] 站点同参。
|
||||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
elts.append(elem)
|
||||
elts.append(count_node)
|
||||
carray: rawptr[AST] = Subscript(ps.pool,
|
||||
Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||||
"CArray", ASTCtx.Load),
|
||||
Tuple(ps.pool, elts, ASTCtx.Load),
|
||||
ASTCtx.Load)
|
||||
_set_pos(carray, lb_lineno, lb_col, 0, 0)
|
||||
return carray
|
||||
|
||||
|
||||
def _parse_array_prefix_declarator(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""C 风格前置数组声明符 `[N]T` / `[]T` → `t.CArray[T, N]` / `t.CArray[T, None]`
|
||||
|
||||
只在注解位置(_parse_type_ann)消费;当前 token 不是 '[' 时返回 None。
|
||||
脱糖目标与 TPC / StubGen / DeclarationGenerator 的规范数组形态同形
|
||||
(`t.CArray[E, N]`;无长度按 `t.CArray[E, None]` = 指针模式),即把
|
||||
`coeffs: const [8]double` 就地等价成文档给出的修法
|
||||
`coeffs: const t.CArray[double, 8]`(Debugger/type-descriptors.md §3 第 6 例)。
|
||||
|
||||
必须在 `_parse_test` 之前调用:否则 `[N]` 会被解析成 list 字面量,随后在元素
|
||||
类型标识符处报"意外的 token"(includes/binascii.vp `HEX_VALS: [256]int` 现象)。
|
||||
"""
|
||||
if _match_op(ps, TokOp.Lsqb) == 0:
|
||||
return None
|
||||
lb_lineno: int = _cur_lineno(ps)
|
||||
lb_col: int = _cur_col(ps)
|
||||
_advance(ps) # 消费 '['
|
||||
count_node: rawptr[AST] = None
|
||||
if _match_op(ps, TokOp.Rsqb) == 0:
|
||||
# 长度表达式(常量/常量表达式;非法长度由下游 ArrayDims 判据拒绝)
|
||||
count_node = _parse_test(ps)
|
||||
if count_node is None:
|
||||
_record_parse_err(ps)
|
||||
return None
|
||||
if _match_op(ps, TokOp.Rsqb) == 0:
|
||||
_record_parse_err(ps) # 缺少 ']'
|
||||
ps.error_count += 1
|
||||
return None
|
||||
_advance(ps) # 消费 ']'
|
||||
# 元素类型:递归 _parse_type_ann,故 `[N]const int` / `[N]int*` 走同一规则源
|
||||
elem: rawptr[AST] = _parse_type_ann(ps)
|
||||
if elem is None:
|
||||
return None
|
||||
if count_node is None:
|
||||
count_node = _new_const_none(ps.pool, lb_lineno, lb_col)
|
||||
# 元素大小必须显式传 8(list[T](pool, elem_size) 按字节步长寻址;
|
||||
# 传 2 会让 8 字节指针按 2 字节步长写入 → 堆越界,ASan 在 _set_parent_list
|
||||
# 抛 access-violation)。与文件内其它 list[rawptr[AST]] 站点同参。
|
||||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
elts.append(elem)
|
||||
elts.append(count_node)
|
||||
carray: rawptr[AST] = Subscript(ps.pool,
|
||||
Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||||
"CArray", ASTCtx.Load),
|
||||
Tuple(ps.pool, elts, ASTCtx.Load),
|
||||
ASTCtx.Load)
|
||||
_set_pos(carray, lb_lineno, lb_col, 0, 0)
|
||||
return carray
|
||||
|
||||
|
||||
def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""解析类型注解,支持类型描述符前缀(const / volatile)与 C 风格指针后缀(* / **)。
|
||||
|
||||
|
||||
+34
-47
@@ -8,53 +8,40 @@ def chr(i: int) -> char: return char(i)
|
||||
def min(a: double, b: double) -> double: return a if a < b else b
|
||||
def max(a: double, b: double) -> double: return a if a > b else b
|
||||
|
||||
# rawptr 已内置于编译器(TPC/TPV 双端),作为语法保留字,不可重定义:
|
||||
# rawptr[T] 固定表示"裸指针,指向原始类型 T"(等价 t.CPtr[T])。
|
||||
sizet: t.CTypedef = t.CSizeT
|
||||
size_t: t.CTypedef = sizet
|
||||
uzt: t.CTypedef = sizet
|
||||
int8: t.CTypedef = t.CInt8T
|
||||
int16: t.CTypedef = t.CInt16T
|
||||
int32: t.CTypedef = t.CInt32T
|
||||
int64: t.CTypedef = t.CInt64T
|
||||
uint8: t.CTypedef = t.CUInt8T
|
||||
uint16: t.CTypedef = t.CUInt16T
|
||||
uint32: t.CTypedef = t.CUInt32T
|
||||
uint64: t.CTypedef = t.CUInt64T
|
||||
typedef sizet = t.CSizeT
|
||||
typedef size_t = sizet
|
||||
typedef int8 = t.CInt8T
|
||||
typedef int16 = t.CInt16T
|
||||
typedef int32 = t.CInt32T
|
||||
typedef int64 = t.CInt64T
|
||||
typedef uint8 = t.CUInt8T
|
||||
typedef uint16 = t.CUInt16T
|
||||
typedef uint32 = t.CUInt32T
|
||||
typedef uint64 = t.CUInt64T
|
||||
|
||||
intfast8: t.CTypedef = t.CIntFast8T
|
||||
intfast16: t.CTypedef = t.CIntFast16T
|
||||
intfast32: t.CTypedef = t.CIntFast32T
|
||||
intfast64: t.CTypedef = t.CIntFast64T
|
||||
uintfast8: t.CTypedef = t.CUIntFast8T
|
||||
uintfast16: t.CTypedef = t.CUIntFast16T
|
||||
uintfast32: t.CTypedef = t.CUIntFast32T
|
||||
uintfast64: t.CTypedef = t.CUIntFast64T
|
||||
typedef intfast8 = t.CIntFast8T
|
||||
typedef intfast16 = t.CIntFast16T
|
||||
typedef intfast32 = t.CIntFast32T
|
||||
typedef intfast64 = t.CIntFast64T
|
||||
typedef uintfast8 = t.CUIntFast8T
|
||||
typedef uintfast16 = t.CUIntFast16T
|
||||
typedef uintfast32 = t.CUIntFast32T
|
||||
typedef uintfast64 = t.CUIntFast64T
|
||||
|
||||
intleast8: t.CTypedef = t.CIntLeast8T
|
||||
intleast16: t.CTypedef = t.CIntLeast16T
|
||||
intleast32: t.CTypedef = t.CIntLeast32T
|
||||
intleast64: t.CTypedef = t.CIntLeast64T
|
||||
uintleast8: t.CTypedef = t.CUIntLeast8T
|
||||
uintleast16: t.CTypedef = t.CUIntLeast16T
|
||||
uintleast32: t.CTypedef = t.CUIntLeast32T
|
||||
uintleast64: t.CTypedef = t.CUIntLeast64T
|
||||
typedef intleast8 = t.CIntLeast8T
|
||||
typedef intleast16 = t.CIntLeast16T
|
||||
typedef intleast32 = t.CIntLeast32T
|
||||
typedef intleast64 = t.CIntLeast64T
|
||||
typedef uintleast8 = t.CUIntLeast8T
|
||||
typedef uintleast16 = t.CUIntLeast16T
|
||||
typedef uintleast32 = t.CUIntLeast32T
|
||||
typedef uintleast64 = t.CUIntLeast64T
|
||||
|
||||
# C 基础类型名裸名别名(裸名家族,不随 import 注入;编译器最后匹配本文件兜底解析)
|
||||
# 目标 x86_64-pc-windows-gnu(Windows/mingw-w64,LLP64):
|
||||
# uchar = 8 位无符号(t.CUnsignedChar,固定 Size=8;C 的 unsigned char)
|
||||
# short = 16 位有符号(t.CShort,固定 Size=16)
|
||||
# ushort = 16 位无符号(t.CUnsignedShort,固定 Size=16)
|
||||
# uint = u32,32 位无符号(等价 uint32/u32,底层 t.CUnsignedInt Size=32)
|
||||
# long = 32 位有符号(t.CLong,Size 由 t.configure_platform 依 triple 置 32)
|
||||
# ulong = 32 位无符号(t.CUnsignedLong,Size 由 t.configure_platform 依 triple 置 32)
|
||||
# longlong = 64 位有符号(t.CLongLong,固定 Size=64)
|
||||
# ulonglong = 64 位无符号(t.CUnsignedLongLong,固定 Size=64)
|
||||
uchar: t.CTypedef = t.CUnsignedChar
|
||||
short: t.CTypedef = t.CShort
|
||||
ushort: t.CTypedef = t.CUnsignedShort
|
||||
uint: t.CTypedef = t.CUnsignedInt
|
||||
long: t.CTypedef = t.CLong
|
||||
ulong: t.CTypedef = t.CUnsignedLong
|
||||
longlong: t.CTypedef = t.CLongLong
|
||||
ulonglong: t.CTypedef = t.CUnsignedLongLong
|
||||
typedef uchar = t.CUnsignedChar
|
||||
typedef short = t.CShort
|
||||
typedef ushort = t.CUnsignedShort
|
||||
typedef uint = t.CUnsignedInt
|
||||
typedef long = t.CLong
|
||||
typedef ulong = t.CUnsignedLong
|
||||
typedef longlong = t.CLongLong
|
||||
typedef ulonglong = t.CUnsignedLongLong
|
||||
-1742
File diff suppressed because it is too large.
Load diff
@@ -67,7 +67,7 @@ class GzipFile:
|
||||
self.err = -5 # 读取失败或被截断
|
||||
return
|
||||
# gzip 魔数校验
|
||||
ub: uchar*= buf
|
||||
ub: uchar* = buf
|
||||
if ub[0] != 0x1F or ub[1] != 0x8B or ub[2] != 8:
|
||||
stdlib.free(buf)
|
||||
self.err = -6
|
||||
|
||||
+7
-7
@@ -17,7 +17,7 @@ define md5_I(x: uint32, y: uint32, z: uint32) -> uint32: return y ^ (x | ~z)
|
||||
define md5_rotl(x: uint32, n: int) -> uint32: return (x << n) | (x >> (32 - n))
|
||||
|
||||
# MD5 constant table
|
||||
md5_T: t.CArray[uint32, 64] = [
|
||||
md5_T: const [64]uint32 = [
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
@@ -28,7 +28,7 @@ md5_T: t.CArray[uint32, 64] = [
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
||||
]
|
||||
|
||||
md5_S: t.CArray[int, 64] = [
|
||||
md5_S: const [64]int = [
|
||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
@@ -38,12 +38,12 @@ md5_S: t.CArray[int, 64] = [
|
||||
|
||||
# MD5 Context
|
||||
class md5:
|
||||
state: t.CArray[uint32, 4] = [
|
||||
state: [4]uint32 = [
|
||||
0x67452301, 0xefcdab89,
|
||||
0x98badcfe, 0x10325476
|
||||
] # 4 32-bit state registers
|
||||
count: uint64 = 0 # processed bits
|
||||
buf: t.CArray[uint8, MD5_BLOCK_LEN] # Group buffer
|
||||
buf: [MD5_BLOCK_LEN]uint8
|
||||
# Initialize MD5 context
|
||||
def __init__(self):
|
||||
string.memset(self.buf, 0, MD5_BLOCK_LEN)
|
||||
@@ -53,7 +53,7 @@ class md5:
|
||||
b: uint32 = self.state[1]
|
||||
c: uint32 = self.state[2]
|
||||
d: uint32 = self.state[3]
|
||||
w: t.CArray[uint32, 16]
|
||||
w: [16]uint32
|
||||
# Byte order 32-bit words
|
||||
for i in range(16):
|
||||
w[i] = ((uint32(block[i * 4 + 0]) << 0) |
|
||||
@@ -85,7 +85,7 @@ class md5:
|
||||
self.state[3] += d
|
||||
# Batch input data
|
||||
def update(self, s: str):
|
||||
data: rawptr[uint8] = uint8(s, t.CPtr)
|
||||
data: uint8* = uint8*(s)
|
||||
length: sizet = len(s)
|
||||
idx: sizet = self.count % MD5_BLOCK_LEN
|
||||
self.count += length
|
||||
@@ -104,7 +104,7 @@ class md5:
|
||||
if length > 0:
|
||||
string.memcpy(self.buf + idx, data, length)
|
||||
# End calculation, output 16-byte digest
|
||||
def final(self, out: t.CArray[uint8, MD5_DIGEST_LEN]):
|
||||
def final(self, out: [MD5_DIGEST_LEN]uint8):
|
||||
idx: sizet = self.count % MD5_BLOCK_LEN
|
||||
# Pad 0x80 end
|
||||
self.buf[idx] = 0x80
|
||||
|
||||
+5
-5
@@ -17,18 +17,18 @@ define sha1_f3(b: uint32, c: uint32, d: uint32) -> uint32: return (b & c) | (b &
|
||||
|
||||
# SHA1 Context
|
||||
class sha1:
|
||||
state: t.CArray[uint32, 5] = [
|
||||
state: [5]uint32 = [
|
||||
0x67452301, 0xefcdab89,
|
||||
0x98badcfe, 0x10325476,
|
||||
0xc3d2e1f0
|
||||
] # 5 32-bit state registers
|
||||
count: uint64 = 0 # processed bits
|
||||
buf: t.CArray[uint8, SHA1_BLOCK_LEN]
|
||||
buf: [SHA1_BLOCK_LEN]uint8
|
||||
def __init__(self):
|
||||
string.memset(self.buf, 0, SHA1_BLOCK_LEN)
|
||||
# Single-group compression transform
|
||||
def transform(self, block: rawptr[uint8]):
|
||||
w: t.CArray[uint32, 80]
|
||||
w: [80]uint32
|
||||
data: rawptr[uint8] = block
|
||||
# First 16 words
|
||||
for i in range(16):
|
||||
@@ -77,7 +77,7 @@ class sha1:
|
||||
self.state[3] += d
|
||||
self.state[4] += e
|
||||
def update(self, s: str):
|
||||
data: rawptr[uint8] = uint8(s, t.CPtr)
|
||||
data: uint8* = uint8*(s)
|
||||
length: sizet = len(s)
|
||||
idx: sizet = self.count % SHA1_BLOCK_LEN
|
||||
self.count += length
|
||||
@@ -95,7 +95,7 @@ class sha1:
|
||||
length -= SHA1_BLOCK_LEN
|
||||
if length > 0:
|
||||
string.memcpy(self.buf + idx, data, length)
|
||||
def final(self, out: t.CArray[uint8, SHA1_DIGEST_LEN]):
|
||||
def final(self, out: [SHA1_DIGEST_LEN]uint8):
|
||||
idx: sizet = self.count % SHA1_BLOCK_LEN
|
||||
self.buf[idx] = 0x80
|
||||
idx += 1
|
||||
|
||||
+6
-6
@@ -9,7 +9,7 @@ define SHA256_BLOCK_LEN = 64
|
||||
define SHA256_DIGEST_LEN = 32
|
||||
|
||||
# SHA256 Constants K
|
||||
sha256_K: t.CArray[uint32, 64] = [
|
||||
sha256_K: const [64]uint32 = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
@@ -30,20 +30,20 @@ define Maj(x: uint32, y: uint32, z: uint32) -> uint32: return (x & y) ^ (x & z)
|
||||
|
||||
# SHA256 Context
|
||||
class sha256:
|
||||
state: array[uint32, 8] = [
|
||||
state: [8]uint32 = [
|
||||
0x6a09e667, 0xbb67ae85,
|
||||
0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c,
|
||||
0x1f83d9ab, 0x5be0cd19
|
||||
] # 8 32-bit state registers
|
||||
count: uint64 = 0 # processed bits
|
||||
buf: t.CArray[uint8, SHA256_BLOCK_LEN]
|
||||
buf: [SHA256_BLOCK_LEN]uint8
|
||||
def __init__(self):
|
||||
string.memset(self, 0, sha256.__sizeof__())
|
||||
# string.memset(self.buf, 0, SHA256_BLOCK_LEN)
|
||||
# SHA256 single-group compression transform
|
||||
def transform(self, block: rawptr[uint8]):
|
||||
w: t.CArray[uint32, 64]
|
||||
w: [64]uint32
|
||||
for i in range(16):
|
||||
w[i] = ((uint32(block[i * 4 + 0]) << 24) |
|
||||
(uint32(block[i * 4 + 1]) << 16) |
|
||||
@@ -80,7 +80,7 @@ class sha256:
|
||||
self.state[6] += g
|
||||
self.state[7] += h
|
||||
def update(self, s: str):
|
||||
data: rawptr[uint8] = uint8(s, t.CPtr)
|
||||
data: uint8* = uint8*(s)
|
||||
length: sizet = len(s)
|
||||
idx: sizet = self.count % SHA256_BLOCK_LEN
|
||||
self.count += length
|
||||
@@ -98,7 +98,7 @@ class sha256:
|
||||
length -= SHA256_BLOCK_LEN
|
||||
if length > 0:
|
||||
string.memcpy(self.buf + idx, data, length)
|
||||
def final(self, out: t.CArray[uint8, SHA256_DIGEST_LEN]):
|
||||
def final(self, out: [SHA256_DIGEST_LEN]uint8):
|
||||
idx: sizet = self.count % SHA256_BLOCK_LEN
|
||||
self.buf[idx] = 0x80
|
||||
idx += 1
|
||||
|
||||
+8
-8
@@ -17,7 +17,7 @@ define sha512_BigSigma1(x: uint64) -> uint64: return sha512_ror(x, 14) ^ sha512_
|
||||
define sha512_Sigma0(x: uint64) -> uint64: return sha512_ror(x, 1) ^ sha512_ror(x, 8) ^ sha512_shr(x, 7)
|
||||
define sha512_Sigma1(x: uint64) -> uint64: return sha512_ror(x, 19) ^ sha512_ror(x, 61) ^ sha512_shr(x, 6)
|
||||
|
||||
sha512_K: t.CArray[uint64, 80] = [
|
||||
sha512_K: const [80]uint64 = [
|
||||
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
|
||||
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
|
||||
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
|
||||
@@ -42,18 +42,18 @@ sha512_K: t.CArray[uint64, 80] = [
|
||||
|
||||
# SHA512 Context
|
||||
class sha512:
|
||||
state: t.CArray[uint64, 8] = [
|
||||
state: [8]uint64 = [
|
||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
|
||||
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1, 0x9b05688c2b3e6c1f,
|
||||
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
|
||||
]
|
||||
count: t.CArray[uint64, 2] = [0, 0]
|
||||
buf: t.CArray[uint8, SHA512_BLOCK_LEN]
|
||||
count: [2]uint64 = [0, 0]
|
||||
buf: [SHA512_BLOCK_LEN]uint8
|
||||
def __init__(self):
|
||||
string.memset(self, 0, sha512.__sizeof__())
|
||||
def transform(self, block: t.CArray[uint8, SHA512_BLOCK_LEN]):
|
||||
w: t.CArray[uint64, 80]
|
||||
def transform(self, block: [SHA512_BLOCK_LEN]uint8):
|
||||
w: [80]uint64
|
||||
a: uint64
|
||||
b: uint64
|
||||
c: uint64
|
||||
@@ -99,7 +99,7 @@ class sha512:
|
||||
self.state[6] += g
|
||||
self.state[7] += h
|
||||
def update(self, s: str):
|
||||
data: rawptr[uint8] = uint8(s, t.CPtr)
|
||||
data: uint8* = uint8*(s)
|
||||
length: sizet = len(s)
|
||||
idx: uint32 = uint32(self.count[0] % SHA512_BLOCK_LEN)
|
||||
self.count[0] += length
|
||||
@@ -119,7 +119,7 @@ class sha512:
|
||||
length -= SHA512_BLOCK_LEN
|
||||
if length > 0:
|
||||
string.memcpy(self.buf + idx, data, length)
|
||||
def final(self, out: t.CArray[uint8, SHA512_DIGEST_LEN]):
|
||||
def final(self, out: [SHA512_DIGEST_LEN]uint8):
|
||||
bits_lo: uint64 = self.count[0] << 3
|
||||
bits_hi: uint64 = (self.count[1] << 3) | (self.count[0] >> 61)
|
||||
idx: uint32 = uint32(self.count[0] % SHA512_BLOCK_LEN)
|
||||
|
||||
+69
-5
@@ -25,7 +25,7 @@ from .__function import Function, BasicBlock, block_append_text, block_set_termi
|
||||
# r = builder.build_load(i32_ty, a)
|
||||
# builder.build_ret(r)
|
||||
#
|
||||
# 注意: 加 @t.NoVTable 以与硬编码 BUILDER_SIZE=32 保持一致
|
||||
# 注意: 加 @t.NoVTable 以与硬编码 BUILDER_SIZE=40 保持一致
|
||||
# (方法以实例方法实现,无虚表;文件末尾的同名顶层函数为一行委托)。
|
||||
# ============================================================
|
||||
novirtual class IRBuilder:
|
||||
@@ -33,6 +33,7 @@ novirtual class IRBuilder:
|
||||
Func: rawptr[Function] # 所属函数
|
||||
CurBlock: rawptr[BasicBlock] # 当前插入点
|
||||
Counter: int # SSA 名计数器
|
||||
DbgLoc: str # current DILocation ref (e.g. "!5"; None = no !dbg on emitted instructions)
|
||||
|
||||
# ============================================================
|
||||
# 内部辅助
|
||||
@@ -75,9 +76,31 @@ novirtual class IRBuilder:
|
||||
|
||||
|
||||
def _emit(self, text: str):
|
||||
"""将指令文本追加到当前块"""
|
||||
"""Append instruction text to the current block.
|
||||
|
||||
When DbgLoc is set, append a ", !dbg !N" suffix. This is the single
|
||||
write funnel, so all build_* paths get it. Multi-line text (switch)
|
||||
keeps the suffix after the final ']', never on an inner line; a
|
||||
trailing newline is handled too (see _append_dbg_suffix).
|
||||
"""
|
||||
if self is None or self.CurBlock is None: return
|
||||
block_append_text(self.Pool, self.CurBlock, text)
|
||||
out: str = text
|
||||
if self.DbgLoc is not None:
|
||||
out = _append_dbg_suffix(self.Pool, text, self.DbgLoc)
|
||||
block_append_text(self.Pool, self.CurBlock, out)
|
||||
|
||||
def set_debug_location(self, ref: str):
|
||||
"""Set the current DILocation ref (e.g. "!5"); later instructions get ", !dbg !5".
|
||||
|
||||
ref=None clears it (default: byte-identical to output without debug info).
|
||||
"""
|
||||
if self is None: return
|
||||
self.DbgLoc = ref
|
||||
|
||||
def clear_debug_location(self):
|
||||
"""Clear the current DILocation ref (later instructions carry no !dbg)"""
|
||||
if self is None: return
|
||||
self.DbgLoc = None
|
||||
|
||||
|
||||
def _mark_terminated(self):
|
||||
@@ -1164,8 +1187,8 @@ novirtual class IRBuilder:
|
||||
# ============================================================
|
||||
# 工厂函数
|
||||
# ============================================================
|
||||
# IRBuilder 硬编码大小: Pool(8) + Func(8) + CurBlock(8) + Counter(4) = 32 字节
|
||||
BUILDER_SIZE: t.CDefine = 32
|
||||
# IRBuilder 硬编码大小: Pool(8) + Func(8) + CurBlock(8) + Counter(4) + pad(4) + DbgLoc(8) = 40 字节
|
||||
BUILDER_SIZE: t.CDefine = 40
|
||||
|
||||
def new_builder(pool: memhub.MemBuddy*, func: rawptr[Function]) -> rawptr[IRBuilder]:
|
||||
"""创建 IRBuilder,关联到函数 func"""
|
||||
@@ -1176,6 +1199,7 @@ def new_builder(pool: memhub.MemBuddy*, func: rawptr[Function]) -> rawptr[IRBuil
|
||||
ptr.Func = func
|
||||
ptr.CurBlock = None
|
||||
ptr.Counter = 0
|
||||
ptr.DbgLoc = None
|
||||
return ptr
|
||||
|
||||
|
||||
@@ -1213,6 +1237,46 @@ def _emit(builder: rawptr[IRBuilder], text: str):
|
||||
builder._emit(text)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _append_dbg_suffix - append a ", !dbg !N" location suffix to instruction text
|
||||
#
|
||||
# Single-line: appended at the end. Multi-line (switch): appended after the
|
||||
# whole text (after the final ']'), never on an inner line; a trailing
|
||||
# newline has the suffix inserted before it.
|
||||
# ============================================================
|
||||
def _append_dbg_suffix(pool: memhub.MemBuddy*, text: str, loc: str) -> str:
|
||||
"""Return text + " , !dbg !N" (suffix after the last line for multi-line text)"""
|
||||
if text is None or loc is None:
|
||||
return text
|
||||
tlen: sizet = string.strlen(text)
|
||||
slen: sizet = string.strlen(loc)
|
||||
# room: ins(<=tlen) + ", !dbg "(7) + loc(slen) + optional '\n' + NUL
|
||||
buf: str = pool.alloc(tlen + slen + 16)
|
||||
if buf is None:
|
||||
return text
|
||||
# insert before a trailing newline (multi-line instruction case)
|
||||
ins: sizet = tlen
|
||||
if tlen > 0 and text[tlen - 1] == '\n':
|
||||
ins = tlen - 1
|
||||
string.strncpy(buf, text, ins)
|
||||
buf[ins] = '\0'
|
||||
string.strcat(buf, ", !dbg ")
|
||||
string.strcat(buf, loc)
|
||||
if ins < tlen:
|
||||
string.strcat(buf, "\n")
|
||||
return buf
|
||||
|
||||
|
||||
def builder_set_debug_location(builder: rawptr[IRBuilder], ref: str):
|
||||
"""Set the builder's current DILocation ref (e.g. "!5"; later instructions get ", !dbg !5")"""
|
||||
builder.set_debug_location(ref)
|
||||
|
||||
|
||||
def builder_clear_debug_location(builder: rawptr[IRBuilder]):
|
||||
"""Clear the builder's current DILocation ref"""
|
||||
builder.clear_debug_location()
|
||||
|
||||
|
||||
def _mark_terminated(builder: rawptr[IRBuilder]):
|
||||
"""标记当前块已终止"""
|
||||
builder._mark_terminated()
|
||||
|
||||
+19
-2
@@ -85,6 +85,7 @@ novirtual class Function(GSListNode[Function]):
|
||||
AttrGroup: str # 字符串属性组内容(如 '"section"=".text.startup"',None=无)
|
||||
AttrGroupId: int # 属性组编号(>0 时函数行输出 #N,模块尾输出 attributes #N)
|
||||
NameLen: sizet # 函数名长度(创建时一次算好,恒等于 strlen(Name);追加在末尾,既有字段偏移不变)
|
||||
DbgRef: str # this function's DISubprogram ref (e.g. "!3"; None = no debug, emits " !dbg !3")
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -145,8 +146,8 @@ def new_param(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
|
||||
return ptr
|
||||
|
||||
|
||||
# Function 硬编码大小: Next(8) + Name(8) + RetTy(8) + Params(8) + Blocks(8) + IsDeclared(4) + IsVarArg(4) + Attrs(8) + AttrGroup(8) + AttrGroupId(8) + NameLen(8) = 80 字节
|
||||
FUNC_SIZE: t.CDefine = 80
|
||||
# Function 硬编码大小: Next(8) + Name(8) + RetTy(8) + Params(8) + Blocks(8) + IsDeclared(4) + IsVarArg(4) + Attrs(8) + AttrGroup(8) + AttrGroupId(8) + NameLen(8) + DbgRef(8) = 88 字节
|
||||
FUNC_SIZE: t.CDefine = 88
|
||||
|
||||
def new_function(pool: memhub.MemBuddy*, name: str,
|
||||
ret_ty: rawptr[LLVMType]) -> rawptr[Function]:
|
||||
@@ -162,6 +163,7 @@ def new_function(pool: memhub.MemBuddy*, name: str,
|
||||
ptr.Attrs = None
|
||||
ptr.AttrGroup = None
|
||||
ptr.AttrGroupId = 0
|
||||
ptr.DbgRef = None
|
||||
# NameLen 创建时一次算好:Name 终生不变(本文件仅此处赋值 ptr.Name),
|
||||
# 故 NameLen 恒等于 strlen(Name),可替代对函数名的反复 strlen 热点。
|
||||
# name=None 时置 0,与 new_line 的 "text is not None" 判空约定一致
|
||||
@@ -340,6 +342,16 @@ def param_set_attrs(param: rawptr[Param], attrs: str):
|
||||
param.Attrs = attrs
|
||||
|
||||
|
||||
def function_set_dbg(func: rawptr[Function], ref: str):
|
||||
"""Set the function-level debug ref (DISubprogram node ref, e.g. "!3").
|
||||
|
||||
Define only: FunctionPrint emits " !dbg <ref>" after the signature line.
|
||||
ref=None clears it (default: byte-identical to output without debug info).
|
||||
"""
|
||||
if func is None: return
|
||||
func.DbgRef = ref
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _ll_name_needs_quote - 检查 LLVM 标识符是否需要加引号
|
||||
#
|
||||
@@ -1218,6 +1230,11 @@ def FunctionPrint(buf: str, size: sizet,
|
||||
_append_attr_group_def(buf, size, pool, func)
|
||||
return
|
||||
|
||||
# function-level debug location (define only; declare has no body, no !dbg)
|
||||
if func.DbgRef is not None:
|
||||
_append_cstr(buf, size, " !dbg ")
|
||||
_append_cstr(buf, size, func.DbgRef)
|
||||
|
||||
# 函数体
|
||||
_append_cstr(buf, size, " {\n")
|
||||
|
||||
|
||||
@@ -26,16 +26,19 @@ from .__function import (
|
||||
block_append_line, block_append_text,
|
||||
function_set_attrs, param_set_attrs,
|
||||
function_set_attr_group,
|
||||
function_set_dbg,
|
||||
function_get_name, function_get_name_len, function_get_ret_ty, function_get_params,
|
||||
function_get_param_head, function_get_next, function_is_declared,
|
||||
param_get_name, param_get_ty, param_get_next,
|
||||
FunctionPrint, G_PeepEnable,
|
||||
)
|
||||
from .__module import (
|
||||
LLVMModule, GlobalVariable, NamedTypeNode, new_module, new_global_variable,
|
||||
LLVMModule, GlobalVariable, NamedTypeNode, DINode, DINamedMeta,
|
||||
new_module, new_global_variable,
|
||||
module_add_function, module_add_global, module_add_named_type,
|
||||
module_set_named_type_packed,
|
||||
module_set_target, module_set_datalayout,
|
||||
module_add_di_node, module_add_named_metadata,
|
||||
global_set_constant, global_set_linkage, global_set_unnamed_addr, global_set_section,
|
||||
LLVMModulePrint, OUTPUT_FULL, OUTPUT_STUB, OUTPUT_TEXT,
|
||||
module_ensure_opaque_for_type,
|
||||
@@ -43,6 +46,7 @@ from .__module import (
|
||||
from .__builder import (
|
||||
IRBuilder, new_builder, position_at_end,
|
||||
builder_cur_block_is_terminated,
|
||||
builder_set_debug_location, builder_clear_debug_location,
|
||||
build_alloca, build_alloca_aligned, build_alloca_at_entry, build_load, build_store,
|
||||
build_add, build_sub, build_mul, build_sdiv, build_udiv, build_srem, build_urem,
|
||||
build_and, build_or, build_xor, build_shl, build_lshr, build_ashr,
|
||||
|
||||
+180
-4
@@ -40,6 +40,27 @@ class NamedTypeNode(GSListNode[NamedTypeNode]):
|
||||
IsPacked: int # 1=packed 结构体(输出 <{...}>,无字段填充)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# DINode: DI metadata node, emitted as "!N = <Text>".
|
||||
#
|
||||
# Text is the node body (may contain !M references to other nodes).
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class DINode(GSListNode[DINode]):
|
||||
Text: str # node body, e.g. "!DIFile(filename: \"a.vp\", directory: \"/x\")"
|
||||
|
||||
|
||||
# ============================================================
|
||||
# DINamedMeta: named metadata, emitted as "!<Name> = !{ <Refs> }".
|
||||
#
|
||||
# Refs is the brace body (e.g. "!1" / "!1, !2").
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class DINamedMeta(GSListNode[DINamedMeta]):
|
||||
Name: str # e.g. "llvm.dbg.cu" / "llvm.module.flags"
|
||||
Refs: str # reference list text, e.g. "!1"
|
||||
|
||||
|
||||
# ============================================================
|
||||
# LLVMModule: LLVM IR 模块容器
|
||||
#
|
||||
@@ -62,7 +83,13 @@ class LLVMModule:
|
||||
NamedTypeHead: rawptr[NamedTypeNode] # 命名结构体类型链表头
|
||||
NamedTypeTail: rawptr[NamedTypeNode] # 命名结构体类型链表尾
|
||||
NamedTypeCount: sizet # 命名结构体类型计数
|
||||
# 总计: 12 字段 * 8 = 96 字节(无 vtable)
|
||||
DIMetaHead: rawptr[DINode] # DI metadata node list head
|
||||
DIMetaTail: rawptr[DINode] # DI metadata node list tail
|
||||
DIMetaCount: sizet # DI node count (= next !N id)
|
||||
DINamedHead: rawptr[DINamedMeta] # named metadata list head
|
||||
DINamedTail: rawptr[DINamedMeta] # named metadata list tail
|
||||
DINamedCount: sizet # named metadata count
|
||||
# 总计: 18 字段 * 8 = 144 字节(无 vtable)
|
||||
|
||||
# ============================================================
|
||||
# 实例方法(对象化重构:原 module_* 顶层函数改写为方法,
|
||||
@@ -103,6 +130,84 @@ class LLVMModule:
|
||||
if self is None: return
|
||||
self.DataLayout = layout
|
||||
|
||||
# ---- DI debug metadata ----
|
||||
def add_di_node(self, pool: memhub.MemBuddy*, text: str) -> int:
|
||||
"""Append a DI metadata node; return its !N id (0-based, unique), -1 on failure.
|
||||
|
||||
text is the node body incl. !M references built from earlier ids.
|
||||
"""
|
||||
if self is None or pool is None or text is None:
|
||||
return -1
|
||||
node: rawptr[DINode] = pool.alloc(DI_NODE_SIZE)
|
||||
if node is None:
|
||||
return -1
|
||||
string.memset(node, 0, DI_NODE_SIZE)
|
||||
# copy body (caller buffer may be reused)
|
||||
tlen: sizet = string.strlen(text)
|
||||
tcopy: str = pool.alloc(tlen + 1)
|
||||
if tcopy is None:
|
||||
return -1
|
||||
string.strcpy(tcopy, text)
|
||||
node.Text = tcopy
|
||||
if self.DIMetaHead is None:
|
||||
self.DIMetaHead = node
|
||||
else:
|
||||
self.DIMetaTail.Next = node
|
||||
self.DIMetaTail = node
|
||||
node_id: int = int(self.DIMetaCount)
|
||||
self.DIMetaCount += 1
|
||||
return node_id
|
||||
|
||||
def add_named_metadata(self, pool: memhub.MemBuddy*, name: str, refs: str):
|
||||
"""Register named metadata "!<name> = !{ <refs> }"; merge into an existing
|
||||
entry with the same name (refs is the brace body, e.g. "!3")."""
|
||||
if self is None or pool is None or name is None:
|
||||
return
|
||||
cur: rawptr[DINamedMeta] = self.DINamedHead
|
||||
cur_steps: sizet = 0
|
||||
while cur is not None:
|
||||
cur_steps += 1
|
||||
if cur_steps > IR_DUMP_CHAIN_LIMIT:
|
||||
stdio.printf("[IR-DUMP] 链表成环: module di named(DINamedCount=%zu,dump 终止)\n",
|
||||
self.DINamedCount)
|
||||
stdlib.exit(1)
|
||||
if cur.Name is not None and string.strcmp(cur.Name, name) == 0:
|
||||
if refs is not None:
|
||||
old_len: sizet = 0
|
||||
if cur.Refs is not None:
|
||||
old_len = string.strlen(cur.Refs)
|
||||
merged: str = pool.alloc(old_len + string.strlen(refs) + 3)
|
||||
if merged is not None:
|
||||
merged[0] = '\0'
|
||||
if cur.Refs is not None:
|
||||
string.strcat(merged, cur.Refs)
|
||||
string.strcat(merged, ", ")
|
||||
string.strcat(merged, refs)
|
||||
cur.Refs = merged
|
||||
return
|
||||
cur = cur.Next
|
||||
entry: rawptr[DINamedMeta] = pool.alloc(DI_NAMED_SIZE)
|
||||
if entry is None:
|
||||
return
|
||||
string.memset(entry, 0, DI_NAMED_SIZE)
|
||||
ncopy: str = pool.alloc(string.strlen(name) + 1)
|
||||
if ncopy is None:
|
||||
return
|
||||
string.strcpy(ncopy, name)
|
||||
entry.Name = ncopy
|
||||
if refs is not None:
|
||||
rcopy: str = pool.alloc(string.strlen(refs) + 1)
|
||||
if rcopy is None:
|
||||
return
|
||||
string.strcpy(rcopy, refs)
|
||||
entry.Refs = rcopy
|
||||
if self.DINamedHead is None:
|
||||
self.DINamedHead = entry
|
||||
else:
|
||||
self.DINamedTail.Next = entry
|
||||
self.DINamedTail = entry
|
||||
self.DINamedCount += 1
|
||||
|
||||
# ---- 命名结构体类型管理 ----
|
||||
def add_named_type(self, pool: memhub.MemBuddy*, ty: rawptr[LLVMType]):
|
||||
"""将命名结构体类型添加到模块(用于输出 %"name" = type { ... } 定义行)
|
||||
@@ -361,16 +466,60 @@ class LLVMModule:
|
||||
func = func.Next
|
||||
func_idx += 1
|
||||
|
||||
# DI metadata, text/full only: never in stub, since StubMerger concatenates
|
||||
# stub + text into one .ll and stub metadata would duplicate !N / llvm.dbg.cu.
|
||||
# Node defs precede named metadata: ids ascend from 0, so every !M ref is
|
||||
# backward (no forward reference needed).
|
||||
if (mode == OUTPUT_TEXT or mode == OUTPUT_FULL) and self.DIMetaHead is not None:
|
||||
node: rawptr[DINode] = self.DIMetaHead
|
||||
nid: int = 0
|
||||
id_buf: str = pool.alloc(32)
|
||||
node_steps: sizet = 0
|
||||
while node is not None:
|
||||
node_steps += 1
|
||||
if node_steps > IR_DUMP_CHAIN_LIMIT:
|
||||
stdio.printf("[IR-DUMP] 链表成环: module di meta(DIMetaCount=%zu,dump 终止)\n",
|
||||
self.DIMetaCount)
|
||||
stdlib.exit(1)
|
||||
if node.Text is not None and id_buf is not None:
|
||||
viperlib.snprintf(id_buf, 32, "!%d = ", nid)
|
||||
_append_cstr(buf, size, id_buf)
|
||||
_append_cstr(buf, size, node.Text)
|
||||
_append_cstr(buf, size, "\n")
|
||||
node = node.Next
|
||||
nid += 1
|
||||
|
||||
# named metadata "!<name> = !{ <refs> }" (llvm.dbg.cu / llvm.module.flags ...)
|
||||
if (mode == OUTPUT_TEXT or mode == OUTPUT_FULL) and self.DINamedHead is not None:
|
||||
if self.DIMetaHead is not None:
|
||||
_append_cstr(buf, size, "\n")
|
||||
nm: rawptr[DINamedMeta] = self.DINamedHead
|
||||
nm_steps: sizet = 0
|
||||
while nm is not None:
|
||||
nm_steps += 1
|
||||
if nm_steps > IR_DUMP_CHAIN_LIMIT:
|
||||
stdio.printf("[IR-DUMP] 链表成环: module di named(DINamedCount=%zu,dump 终止)\n",
|
||||
self.DINamedCount)
|
||||
stdlib.exit(1)
|
||||
_append_cstr(buf, size, "!")
|
||||
if nm.Name is not None:
|
||||
_append_cstr(buf, size, nm.Name)
|
||||
_append_cstr(buf, size, " = !{ ")
|
||||
if nm.Refs is not None:
|
||||
_append_cstr(buf, size, nm.Refs)
|
||||
_append_cstr(buf, size, " }\n")
|
||||
nm = nm.Next
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 工厂函数
|
||||
# ============================================================
|
||||
def new_module(pool: memhub.MemBuddy*, name: str) -> rawptr[LLVMModule]:
|
||||
"""创建一个模块"""
|
||||
# 硬编码 96 字节:12 字段 * 8 字节(无 vtable,@t.NoVTable)
|
||||
ptr: rawptr[LLVMModule] = pool.alloc(96)
|
||||
# 硬编码 144 字节:18 字段 * 8 字节(无 vtable,@t.NoVTable)
|
||||
ptr: rawptr[LLVMModule] = pool.alloc(144)
|
||||
if ptr is None: return None
|
||||
string.memset(ptr, 0, 96)
|
||||
string.memset(ptr, 0, 144)
|
||||
ptr.Name = name
|
||||
ptr.FuncHead = None
|
||||
ptr.FuncTail = None
|
||||
@@ -383,6 +532,12 @@ def new_module(pool: memhub.MemBuddy*, name: str) -> rawptr[LLVMModule]:
|
||||
ptr.NamedTypeHead = None
|
||||
ptr.NamedTypeTail = None
|
||||
ptr.NamedTypeCount = 0
|
||||
ptr.DIMetaHead = None
|
||||
ptr.DIMetaTail = None
|
||||
ptr.DIMetaCount = 0
|
||||
ptr.DINamedHead = None
|
||||
ptr.DINamedTail = None
|
||||
ptr.DINamedCount = 0
|
||||
return ptr
|
||||
|
||||
|
||||
@@ -392,6 +547,12 @@ GV_SIZE: t.CDefine = 72
|
||||
# NamedTypeNode 硬编码大小: Next(8) + Ty(8) + IsPacked(8) = 24 字节
|
||||
NAMED_TYPE_NODE_SIZE: t.CDefine = 24
|
||||
|
||||
# DINode hardcoded size: Next(8) + Text(8) = 16 bytes
|
||||
DI_NODE_SIZE: t.CDefine = 16
|
||||
|
||||
# DINamedMeta hardcoded size: Next(8) + Name(8) + Refs(8) = 24 bytes
|
||||
DI_NAMED_SIZE: t.CDefine = 24
|
||||
|
||||
def new_global_variable(pool: memhub.MemBuddy*,
|
||||
name: str, ty: rawptr[LLVMType]) -> rawptr[GlobalVariable]:
|
||||
"""创建一个全局变量"""
|
||||
@@ -435,6 +596,21 @@ def module_set_datalayout(mod: rawptr[LLVMModule], layout: str):
|
||||
mod.set_datalayout(layout)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# DI debug metadata
|
||||
# ============================================================
|
||||
def module_add_di_node(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
|
||||
text: str) -> int:
|
||||
"""Append a DI metadata node; return its !N id (-1 on failure)"""
|
||||
return mod.add_di_node(pool, text)
|
||||
|
||||
|
||||
def module_add_named_metadata(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
|
||||
name: str, refs: str):
|
||||
"""Register named metadata "!<name> = !{ <refs> }" """
|
||||
mod.add_named_metadata(pool, name, refs)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 命名结构体类型管理
|
||||
# ============================================================
|
||||
|
||||
+23
-9
@@ -13,10 +13,9 @@ from linkedlist import GSListNode
|
||||
# 继承 GSListNode[ParamNode] 获取强类型 Next: ParamNode|CPtr
|
||||
# Ty 用 rawptr[t\.CVoid] 前向引用,使用时转型为 (rawptr[LLVMType])
|
||||
#
|
||||
# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。
|
||||
# 注意: 必须加 novirtual(理由见 __function.py 的 Line 注释)。
|
||||
# ============================================================
|
||||
@t.NoVTable
|
||||
class ParamNode(GSListNode[ParamNode]):
|
||||
novirtual class ParamNode(GSListNode[ParamNode]):
|
||||
Ty: rawptr # LLVMType*(前向引用,使用时转型)
|
||||
|
||||
|
||||
@@ -56,7 +55,7 @@ class LLVMType(t.REnum):
|
||||
|
||||
# 浮点类型: half/float/double/fp128
|
||||
class Float:
|
||||
Bits: int # 16=half, 32=float, 64=double, 128=fp128
|
||||
Bits: int # 16=half, 32=float, 64=double, 128=fp128
|
||||
|
||||
# 无值类型
|
||||
class Void:
|
||||
@@ -74,11 +73,25 @@ class LLVMType(t.REnum):
|
||||
# ============================================================
|
||||
# 内部分配辅助
|
||||
# ============================================================
|
||||
def _TypeSizeGuard() -> sizet:
|
||||
"""LLVMType 的字节尺寸;为 0 时响亮失败。
|
||||
|
||||
__sizeof__() 在布局未就绪(结构体未注册 / opaque / 字段链未填充)时被
|
||||
算成 0,此时 alloc(0) 只会静默返回 NULL,类型对象随之丢失且无从追查。
|
||||
"""
|
||||
sz: sizet = LLVMType.__sizeof__()
|
||||
if sz == 0:
|
||||
stdio.printf("FATAL: llvmlite.LLVMType.__sizeof__() == 0"
|
||||
"(布局未注册或 opaque),拒绝 alloc(0)\n")
|
||||
stdlib.exit(1)
|
||||
return sz
|
||||
|
||||
|
||||
def _new_type(pool: memhub.MemBuddy*) -> rawptr[LLVMType]:
|
||||
"""从 mpool 分配一个零初始化的 LLVMType"""
|
||||
ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__())
|
||||
if ptr:
|
||||
string.memset(ptr, 0, LLVMType.__sizeof__())
|
||||
sz_nt: sizet = _TypeSizeGuard()
|
||||
ptr: rawptr[LLVMType] = pool.alloc(sz_nt)
|
||||
if ptr: string.memset(ptr, 0, sz_nt)
|
||||
return ptr
|
||||
|
||||
|
||||
@@ -684,10 +697,11 @@ def _DeepCopyDepth(pool: memhub.MemBuddy*,
|
||||
"""
|
||||
if ty is None:
|
||||
return None
|
||||
new_ty: rawptr[LLVMType] = (rawptr[LLVMType])(pool.alloc(LLVMType.__sizeof__()))
|
||||
sz_dc: sizet = _TypeSizeGuard()
|
||||
new_ty: rawptr[LLVMType] = (rawptr[LLVMType])(pool.alloc(sz_dc))
|
||||
if new_ty is None:
|
||||
return None
|
||||
string.memset(new_ty, 0, LLVMType.__sizeof__())
|
||||
string.memset(new_ty, 0, sz_dc)
|
||||
if depth >= DEEPCOPY_MAX_DEPTH:
|
||||
# 深度超限:浅拷贝 tag 后停止递归(复制当前变体但不深拷子类型)
|
||||
match ty:
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
# * Scope and feature set:
|
||||
# * - Fundamental mathematical constants: π, e, π/2, π/4, reciprocal π, 2/π, ln(2), ln(10), 2/sqrt(π), DBL_MAX.
|
||||
# * - Angle conversion: radians ↔ degrees helpers.
|
||||
# * - Trigonometric functions: sin, cos, tan, sinf, cosf, tanf. Argument reduction is performed using subtraction of
|
||||
# * 2π until the value falls into [-π, π]; polynomial approximations are truncated series tuned for the target interval.
|
||||
# * - Trigonometric functions: sin, cos, tan, sinf, cosf, tanf. Argument reduction is O(1): a three-constant
|
||||
# * Cody-Waite step for |x| < 2^20, and a Payne-Hanek step (20-word table of 2/π) above. Both yield r ∈ [-π/4, π/4]
|
||||
# * as a double-double fed to the truncated Taylor series.
|
||||
# * - Inverse trigonometric functions: asin, acos, atan, atan2. asin uses a minimax polynomial suitable for [-1, 1];
|
||||
# * atan relies on a Taylor core limited to |x| ≤ 1 combined with range folding via reciprocal and quadrant logic.
|
||||
# * - Hyperbolic trigonometry: sinh, cosh, tanh, built atop exp and exp(-x).
|
||||
@@ -69,30 +70,33 @@ define U_M_2_SQRT_PI: double = 1.77245385090551602730
|
||||
define DBL_MAX: double = 1.7976931348623157e308
|
||||
define NEG_1E308: double = -1e308
|
||||
|
||||
# Argument reduction constants for sin/cos/tan: pi/2 split into HI+MID+LO and 2/pi (all exact doubles),
|
||||
# plus the 2^20 limit below which Cody-Waite applies (so each fn*HK product stays exact in 53 bits).
|
||||
define U_M_PIO2_HI: double = 1.57079632673412561417e+00
|
||||
define U_M_PIO2_MID: double = 6.07710050650619224932e-11
|
||||
define U_M_PIO2_LO: double = 3.52155986518320018165e-27
|
||||
define U_M_INV_PIO2: double = 6.36619772367581382433e-01
|
||||
define U_M_REDUCE_LIMIT: double = 1048576.0
|
||||
|
||||
def radians(degrees: double) -> double: return degrees * (U_M_PI / 180.0)
|
||||
def degrees(radians: double) -> double: return radians * (180.0 / U_M_PI)
|
||||
|
||||
def sin(x: double) -> double:
|
||||
while x > U_M_PI:
|
||||
x -= 2.0 * U_M_PI
|
||||
while x < -U_M_PI:
|
||||
x += 2.0 * U_M_PI
|
||||
def _sin_core(x: double) -> double:
|
||||
"""sin(r) Taylor series, 8 terms, valid on |r| <= pi/4."""
|
||||
x2: double = x * x
|
||||
result: double = x
|
||||
term: double = x
|
||||
coeffs: const [8]double = [6.0, 20.0, 42.0, 72.0, 110.0, 156.0, 210.0, 272.0]
|
||||
sign: double = -1.0
|
||||
coeffs: const [8]double = [6.0, 20.0, 42.0, 72.0, 110.0, 156.0, 210.0, 272.0]
|
||||
for c in coeffs:
|
||||
term = term * x2 / c
|
||||
result += sign * term
|
||||
sign = -sign
|
||||
return result
|
||||
|
||||
def cos(x: double) -> double:
|
||||
while x > U_M_PI:
|
||||
x -= 2.0 * U_M_PI
|
||||
while x < -U_M_PI:
|
||||
x += 2.0 * U_M_PI
|
||||
|
||||
def _cos_core(x: double) -> double:
|
||||
"""cos(r) Taylor series, 8 terms, valid on |r| <= pi/4."""
|
||||
x2: double = x * x
|
||||
result: double = 1.0
|
||||
term: double = 1.0
|
||||
@@ -104,6 +108,154 @@ def cos(x: double) -> double:
|
||||
sign = -sign
|
||||
return result
|
||||
|
||||
|
||||
def _mul_add(a: uint64, b: uint64, c: uint64, lo: rawptr[uint64], hi: rawptr[uint64]):
|
||||
"""Write (hi:lo) = a*b + c using 32-bit limbs for the full 128-bit product."""
|
||||
a0: uint64 = a & 0xFFFFFFFF
|
||||
a1: uint64 = a >> 32
|
||||
b0: uint64 = b & 0xFFFFFFFF
|
||||
b1: uint64 = b >> 32
|
||||
w0: uint64 = a0 * b0
|
||||
t: uint64 = a1 * b0 + (w0 >> 32)
|
||||
w1: uint64 = t & 0xFFFFFFFF
|
||||
w2: uint64 = t >> 32
|
||||
w1 = a0 * b1 + w1
|
||||
h: uint64 = a1 * b1 + w2 + (w1 >> 32)
|
||||
l: uint64 = (w1 << 32) | (w0 & 0xFFFFFFFF)
|
||||
l2: uint64 = l + c
|
||||
if l2 < l: h = h + 1
|
||||
*lo = l2
|
||||
*hi = h
|
||||
|
||||
|
||||
def _two_prod(a: double, b: double, ph: rawptr[double], pl: rawptr[double]):
|
||||
"""Write a*b as the exact double-double (ph + pl) via Dekker splitting (no FMA required)."""
|
||||
split: double = 134217729.0
|
||||
p: double = a * b
|
||||
ca: double = split * a
|
||||
ahi: double = ca - (ca - a)
|
||||
alo: double = a - ahi
|
||||
cb: double = split * b
|
||||
bhi: double = cb - (cb - b)
|
||||
blo: double = b - bhi
|
||||
*ph = p
|
||||
*pl = ((ahi * bhi - p) + ahi * blo + alo * bhi) + alo * blo
|
||||
|
||||
|
||||
def _rem_pio2(x: double, rh: rawptr[double], rl: rawptr[double]) -> int:
|
||||
"""O(1) reduction of finite x >= 0: x = n*(pi/2) + r with |r| <= pi/4; returns n mod 4, writes r = rh + rl."""
|
||||
if x < U_M_REDUCE_LIMIT:
|
||||
# Cody-Waite three-step with pi/2 = HI + MID + LO; each fn*HK product is exact for fn < 2^20
|
||||
q: double = x * U_M_INV_PIO2
|
||||
k: int = int(q)
|
||||
if q - double(k) > 0.5: k = k + 1
|
||||
fn: double = double(k)
|
||||
r: double = x - fn * U_M_PIO2_HI
|
||||
w1: double = fn * U_M_PIO2_MID
|
||||
y0: double = r - w1
|
||||
w2: double = fn * U_M_PIO2_LO
|
||||
rhv: double = y0 - w2
|
||||
*rh = rhv
|
||||
*rl = ((r - y0) - w1) + ((y0 - rhv) - w2)
|
||||
return k & 3
|
||||
# Payne-Hanek: a 20-word (1280-bit) 2/pi table; multiply the 53-bit mantissa and read off the fraction
|
||||
twopi: const [20]uint64 = [
|
||||
0xA2F9836E4E441529, 0xFC2757D1F534DDC0, 0xDB6295993C439041, 0xFE5163ABDEBBC561,
|
||||
0xB7246E3A424DD2E0, 0x06492EEA09D1921C, 0xFE1DEB1CB129A73E, 0xE88235F52EBB4484,
|
||||
0xE99C7026B45F7E41, 0x3991D639835339F4, 0x9C845F8BBDF9283B, 0x1FF897FFDE05980F,
|
||||
0xEF2F118B5A0A6D1F, 0x6D367ECF27CB09B7, 0x4F463F669E5FEA2D, 0x7527BAC7EBE5F17B,
|
||||
0x3D0739F78A5292EA, 0x6BFB5FB11F8D5D08, 0x56033046FC7B6BAB, 0xF0CFBC209AF4361D
|
||||
]
|
||||
xv: double = x
|
||||
xb: uint64 = 0
|
||||
*&xb = *&xv
|
||||
ef: uint64 = (xb >> 52) & 0x7FF
|
||||
m: uint64 = (xb & 0xFFFFFFFFFFFFF) | 0x10000000000000
|
||||
e: int = int(ef) - 1075
|
||||
shift: int = 1280 - e
|
||||
qw: int = shift >> 6
|
||||
s: int = shift & 63
|
||||
b: [21]uint64
|
||||
carry: uint64 = 0
|
||||
for j in range(20):
|
||||
lo: uint64 = 0
|
||||
hi: uint64 = 0
|
||||
_mul_add(m, twopi[19 - j], carry, &lo, &hi)
|
||||
b[j] = lo
|
||||
carry = hi
|
||||
b[20] = carry
|
||||
lo2: uint64 = b[qw] >> s
|
||||
if s == 63: lo2 = (b[qw] >> 63) | (b[qw + 1] << 1)
|
||||
k: int = int(lo2 & 3)
|
||||
a1: uint64 = b[qw] & ((uint64(1) << s) - 1)
|
||||
a2: uint64 = b[qw - 1]
|
||||
a3: uint64 = b[qw - 2]
|
||||
fhi: uint64 = a2
|
||||
flo: uint64 = a3
|
||||
if s != 0:
|
||||
sh: int = 64 - s
|
||||
fhi = (a1 << sh) | (a2 >> s)
|
||||
flo = (a2 << sh) | (a3 >> s)
|
||||
# fractional part f = fhi*2^-64 + flo*2^-128; split off the top 53 bits so df is exact
|
||||
f53: uint64 = fhi >> 11
|
||||
df: double = double(f53)
|
||||
de: double = double(fhi - (f53 << 11))
|
||||
fh: double = df * 1.1102230246251565e-16
|
||||
fl: double = de * 5.421010862427522e-20 + double(flo) * 2.9387358770557188e-39
|
||||
r0h: double = fh
|
||||
r0l: double = fl
|
||||
n: int = k
|
||||
if fh > 0.5 or (fh == 0.5 and fl > 0.0):
|
||||
n = (k + 1) & 3
|
||||
r0h = fh - 1.0
|
||||
ph: double = 0.0
|
||||
pl: double = 0.0
|
||||
_two_prod(r0h, U_M_PIO2_HI, &ph, &pl)
|
||||
*rh = ph
|
||||
*rl = pl + r0h * U_M_PIO2_MID + r0h * U_M_PIO2_LO + r0l * U_M_PIO2_HI
|
||||
return n
|
||||
|
||||
|
||||
def sin(x: double) -> double:
|
||||
u: uint64 = 0
|
||||
xv: double = x
|
||||
*&u = *&xv
|
||||
ef: uint64 = (u >> 52) & 0x7FF
|
||||
if ef == 0x7FF: return x - x # inf/nan -> NaN
|
||||
neg: int = int(u >> 63)
|
||||
ax: double = x if neg == 0 else -x
|
||||
rh: double = 0.0
|
||||
rl: double = 0.0
|
||||
n: int = _rem_pio2(ax, &rh, &rl)
|
||||
sr: double = _sin_core(rh) + _cos_core(rh) * rl
|
||||
cr: double = _cos_core(rh) - _sin_core(rh) * rl
|
||||
res: double = 0.0
|
||||
if n == 0: res = sr
|
||||
elif n == 1: res = cr
|
||||
elif n == 2: res = -sr
|
||||
else: res = -cr
|
||||
return -res if neg != 0 else res
|
||||
|
||||
|
||||
def cos(x: double) -> double:
|
||||
u: uint64 = 0
|
||||
xv: double = x
|
||||
*&u = *&xv
|
||||
ef: uint64 = (u >> 52) & 0x7FF
|
||||
if ef == 0x7FF: return x - x # inf/nan -> NaN
|
||||
neg: int = int(u >> 63)
|
||||
ax: double = x if neg == 0 else -x
|
||||
rh: double = 0.0
|
||||
rl: double = 0.0
|
||||
n: int = _rem_pio2(ax, &rh, &rl)
|
||||
sr: double = _sin_core(rh) + _cos_core(rh) * rl
|
||||
cr: double = _cos_core(rh) - _sin_core(rh) * rl
|
||||
if n == 0: return cr
|
||||
elif n == 1: return -sr
|
||||
elif n == 2: return -cr
|
||||
else: return sr
|
||||
|
||||
|
||||
def tan(x: double) -> double: return sin(x) / cos(x)
|
||||
|
||||
|
||||
@@ -364,38 +516,8 @@ def sqrtf(x: float) -> float:
|
||||
guess = (guess + x / guess) / 2.0
|
||||
return guess
|
||||
|
||||
def sinf(x: float) -> float:
|
||||
while x > 3.14159265:
|
||||
x -= 6.28318530
|
||||
while x < -3.14159265:
|
||||
x += 6.28318530
|
||||
x2: float = x * x
|
||||
result: float = x
|
||||
term: float = x
|
||||
sign: float = -1.0
|
||||
coeffs: const [8]float = [6.0, 20.0, 42.0, 72.0, 110.0, 156.0, 210.0, 272.0]
|
||||
for c in coeffs:
|
||||
term = term * x2 / c
|
||||
result += sign * term
|
||||
sign = -sign
|
||||
return result
|
||||
|
||||
|
||||
def cosf(x: float) -> float:
|
||||
while x > 3.14159265:
|
||||
x -= 6.28318530
|
||||
while x < -3.14159265:
|
||||
x += 6.28318530
|
||||
x2: float = x * x
|
||||
result: float = 1.0
|
||||
term: float = 1.0
|
||||
sign: float = -1.0
|
||||
coeffs: const [8]float = [2.0, 12.0, 30.0, 56.0, 90.0, 132.0, 182.0, 240.0]
|
||||
for c in coeffs:
|
||||
term = term * x2 / c
|
||||
result += sign * term
|
||||
sign = -sign
|
||||
return result
|
||||
def sinf(x: float) -> float: return float(sin(double(x)))
|
||||
def cosf(x: float) -> float: return float(cos(double(x)))
|
||||
|
||||
def tanf(x: float) -> float:
|
||||
c: float = cosf(x)
|
||||
|
||||
+345
-41
@@ -7,6 +7,12 @@
|
||||
目的:让 TransPyV 侧只需把 `llvmlite.` 前缀重命名为 `mlirlite.` 即可编译,
|
||||
从而在不改内部模型、不改 builders 的前提下切换到 MLIR 后端。
|
||||
|
||||
> 实际切换方式(2026-09):不动 TransPyV 侧任何 `llvmlite.` 调用点,也不动
|
||||
> `includes/llvmlite/**`——工程文件 `project.vpj` 的 `options.target = "mlir"` 会让
|
||||
> 模块名 `llvmlite` 在名称→SHA1 解析处改写为 `mlirlite`(唯一改动点是
|
||||
> `StubMergerIR._FindSha1ByModName`)。开关、回退与缓存标签要求见
|
||||
> [Debugger/BackendSwitch.md](../../Debugger/BackendSwitch.md)。
|
||||
|
||||
> 术语约定:文中"中间文本"指 builders 写进 `Line` 的**指令文本**(仍是 LLVM 语法,
|
||||
> 如 `%0 = add i32 %a, %b`);"MLIR 文本"指 `FunctionPrint` / `LLVMModulePrint`
|
||||
> 最终输出、可喂给 `mlir-translate` / `mlir-opt` 的文本。
|
||||
@@ -20,11 +26,11 @@
|
||||
| 文件 | 内容 | 与 llvmlite 的关系 |
|
||||
|------|------|--------------------|
|
||||
| `__init__.vp` | 公共导出 + 便捷工厂(`create_function`/`const_int*`/`create_global_*` 等)+ `LLVMTypeCore`/`LLVMModuleCore` | **导入清单逐名一致(208 个名字),代码逐行一致**,仅注释不同 |
|
||||
| `__types.vp` | `LLVMType`(REnum) / `ParamNode` / 类型工厂 / `TypePrint` / `DeepCopyType` | 模型与工厂**逐名同形**;`TypePrint` 改为 MLIR 语义,`PrintStructDefinition` 变为空操作 |
|
||||
| `__types.vp` | `LLVMType`(REnum) / `ParamNode` / 类型工厂 / `TypePrint` / `DeepCopyType` | 模型与工厂**逐名同形**;`TypePrint` 改为 MLIR 语义(具名结构体内联为 `!llvm.struct<"name", (…)>`),`PrintStructDefinition` 变为空操作,新增打印期命名结构体登记表(§2.10) |
|
||||
| `__values.vp` | `Value` / `ConstInt`/`ConstNull`/`ConstZero`/`ConstFloat`/`SSAValue` / `ValuePrint` | 模型与工厂同形;仅 `ValuePrint` 改为 MLIR 语义 |
|
||||
| `__function.vp` | `Line`/`BasicBlock`/`Param`/`Function` + 工厂 + `RenumberSSA` + 窥孔 + `FunctionPrint` | 模型、`RenumberSSA`、本地窥孔**逐行一致**;`FunctionPrint` 重写为 MLIR 渲染 |
|
||||
| `__builder.vp` | `IRBuilder` + 全部 `build_*` | **逐行一致**(仅 `_type_str` 的 fail-fast 校验字符集适配 MLIR 类型首字符) |
|
||||
| `__module.vp` | `LLVMModule`/`GlobalVariable`/`NamedTypeNode` + `LLVMModulePrint` | 模型同形;`PrintModule` 与全局渲染改为 MLIR |
|
||||
| `__module.vp` | `LLVMModule`/`GlobalVariable`/`NamedTypeNode` + `LLVMModulePrint` | 模型同形;`PrintModule` 与全局渲染改为 MLIR(定义行丢弃 + 打印期登记表发布,§2.10) |
|
||||
| `__verify.vp` | `VerifyResult`/`verify_function`/`verify_module` | **逐行一致**(校验的是内部模型 + 中间文本,与 MLIR 文本无关) |
|
||||
|
||||
### 1.2 差异只在打印阶段(设计红线)
|
||||
@@ -53,8 +59,11 @@ builders 未改,而它们内嵌的类型一律来自 `IRBuilder._type_str` →
|
||||
|
||||
- `MlirTypeText`(中间类型文本 → MLIR 类型文本)以 **`!llvm.…` / `none` 直通**为主路径,
|
||||
同时保留 LLVM 语法分支(`i8*`、`[N x x]`、`{…}`)以兼容手工构造的中间文本;
|
||||
- 结构体类型在中间文本里就已是完整内联形态 `!llvm.struct<(…)>`,
|
||||
因此**不需要**命名结构体类型登记表,也不存在 `%"name"`(出现即响亮报错);
|
||||
- 结构体类型在中间文本里就已是完整内联形态(匿名 `!llvm.struct<(…)>`、
|
||||
具名 `!llvm.struct<"name", (…)>`,见 §2.10),因此**不需要**在中间文本里
|
||||
出现 `%"name"` 这种"只有名字"的形态;若出现(手工构造 / TPV 注入),
|
||||
`MlirTypeText` 按**模块打印期发布的登记表**回查完整定义后展开为具名内联形态,
|
||||
查不到 / 仍是 opaque ⇒ 响亮报错;
|
||||
- `_mlir_read_type` 的类型读取按 `[] {} () <>` 统一做括号平衡,
|
||||
以正确读出含空格/逗号的 `!llvm.array<…>` / `!llvm.struct<(…)>`。
|
||||
|
||||
@@ -70,7 +79,7 @@ builders 未改,而它们内嵌的类型一律来自 `IRBuilder._type_str` →
|
||||
| `Half/Float/Double/FP128` | `f16/f32/f64/f128` |
|
||||
| `Ptr(_)` | `!llvm.ptr`(不透明指针;pointee 只保留在内部模型里) |
|
||||
| `Array(elem,N)` | `!llvm.array<N x elem>`(嵌套:`!llvm.array<2 x !llvm.array<3 x i8>>`) |
|
||||
| `Struct(fields,N,name)` | `!llvm.struct<(f0, f1, …)>`;空 → `!llvm.struct<()>`(**名字不参与渲染**) |
|
||||
| `Struct(fields,N,name)` | `!llvm.struct<(f0, f1, …)>`;空 → `!llvm.struct<()>`。**具名**(`name` 非 None)→ `!llvm.struct<"name", (f0, f1, …)>`(内联具名体,见 §2.10) |
|
||||
| `Func(ret,params)` | `!llvm.func<ret (p0, p1)>` |
|
||||
| `Void` | `none`(仅必须打印时;函数返回值位置**省略**) |
|
||||
| `Label` / `Metadata` | **v1 不支持 → 响亮报错** |
|
||||
@@ -86,7 +95,7 @@ builders 未改,而它们内嵌的类型一律来自 `IRBuilder._type_str` →
|
||||
| 任意以 `*` 结尾(`i8*`、`i8**`) | `!llvm.ptr` |
|
||||
| `[N x T]` | `!llvm.array<N x T'>` |
|
||||
| `{T1, T2}` | `!llvm.struct<(T1', T2')>` |
|
||||
| `%name`(只有名字的结构体类型) | **v1 不支持 → 响亮报错**(中间文本不应出现该形态) |
|
||||
| `%name`(只有名字的结构体类型) | 按打印期登记表回查完整定义 → `!llvm.struct<"name", (f0, …)>`;未登记 / 仍是 opaque → **响亮报错**(见 §2.10) |
|
||||
| `T (P0, P1)` | `!llvm.func<T' (P0', P1')>` |
|
||||
|
||||
### 2.2 函数 / 块 / 指令
|
||||
@@ -99,6 +108,8 @@ llvm.func @add(%a: i32, %b: i32) -> i32 {
|
||||
```
|
||||
|
||||
- void 返回:`llvm.func @noop() { … llvm.return }`;declare(无体):`llvm.func @f(%x: i32) -> i32`
|
||||
- 变参函数(`func.IsVarArg == 1`):参数表尾追加 `...` ——
|
||||
`llvm.func @printf(%p: !llvm.ptr, ...) -> i32`;无固定参数时是 `llvm.func @f(...) -> i32`
|
||||
- **entry 块不带标签**;后续块 `^<name>:`(块名去掉前导 `%`)
|
||||
- 块参数:`^merge(%7: i32):`(块首连续 phi 变成块参数,phi 行不再输出)
|
||||
|
||||
@@ -110,6 +121,7 @@ llvm.func @add(%a: i32, %b: i32) -> i32 {
|
||||
| `%N = icmp sgt i32 %a, %b` | `%N = llvm.icmp "sgt" %a, %b : i32` |
|
||||
| `%N = fcmp oeq f64 %a, %b` | `%N = llvm.fcmp "oeq" %a, %b : f64` |
|
||||
| `%N = alloca i32` | `%N = llvm.alloca %c0 x i32 : (i32) -> !llvm.ptr`(个数按常量 op 物化) |
|
||||
| `%N = alloca i32, align N` | `%N = llvm.alloca %c0 x i32 {alignment = N : i64} : (i32) -> !llvm.ptr`(对齐必须是 2 的幂常量,见 §2.7) |
|
||||
| `%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` |
|
||||
@@ -121,13 +133,20 @@ llvm.func @add(%a: i32, %b: i32) -> i32 {
|
||||
| `%N = call i32 @f(i32 %a)` | `%N = llvm.call @f(%a) : (i32) -> i32` |
|
||||
| `call none @f(i32 %a)` | `llvm.call @f(%a) : (i32) -> ()`(`Void` 的中间类型文本是 `none`) |
|
||||
| `%N = call i32 %fp(i32 %a)` | `%N = llvm.call %fp(%a) : !llvm.ptr, (i32) -> i32` |
|
||||
| `call none asm sideeffect "nop", ""()` | `llvm.inline_asm has_side_effects "nop", "" : () -> ()`(见 §2.8) |
|
||||
| `%N = call i32 asm sideeffect inteldialect "…", "=r,r"(i32 %v)` | `%N = llvm.inline_asm has_side_effects asm_dialect = intel "…", "=r,r" %v : (i32) -> i32`(见 §2.8) |
|
||||
| `br label %blk` | `llvm.br ^blk`(目标有块参数时 `llvm.br ^merge(%c0 : i32)`) |
|
||||
| `br i1 %c, label %t, label %e` | `llvm.cond_br %c, ^t, ^e` |
|
||||
| `ret i32 %v` / `ret void` | `llvm.return %v : i32` / `llvm.return` |
|
||||
| `unreachable` | `llvm.unreachable` |
|
||||
| `%N = select i1 %c, i32 %a, i32 %b` | `%N = llvm.select %c, %a, %b : i1, i32` |
|
||||
| `switch i32 %v, label %d [i32 0, label %c0]` | `llvm.switch %v, ^d [0: ^c0]`(case 值是属性,不物化) |
|
||||
| `switch i32 %v, label %d [i32 0, label %c0]` | `llvm.switch %v : i32, ^d [0: ^c0]`(**值类型紧随值**;case 值是属性,不物化) |
|
||||
| 目标块带 phi 的 switch | `llvm.switch %v : i32, ^d(%c1 : i32) [0: ^c0(%c0 : i32)]`(与 `br` 同一块实参设施,见 §2.4) |
|
||||
| `%N = atomicrmw add !llvm.ptr %p, i32 %v monotonic` | `%N = llvm.atomicrmw add %p, %v monotonic : !llvm.ptr, i32` |
|
||||
| `%N = va_arg !llvm.ptr %ap, i32` | `%N = "llvm.va_arg"(%ap) : (!llvm.ptr) -> i32`(**generic 形态**,见 §2.9) |
|
||||
| `%N = cmpxchg !llvm.ptr %p, i32 %c, i32 %n acq_rel monotonic` | `%N = llvm.cmpxchg %p, %c, %n acq_rel monotonic : !llvm.ptr, i32` + 两条 `llvm.extractvalue`(见 §2.9) |
|
||||
| `%r = extractvalue {i32, i32} %v, 0` | `%r = llvm.extractvalue %v[0] : !llvm.struct<(i32, i32)>`(**索引写在 `[]` 内**;多级索引 `%v[1, 0]` 同理,见 §2.8) |
|
||||
| 变参调用 `%N = call i32 (i8*, i32, ...) @printf(i8* %p, i32 %n)` | `%N = llvm.call @printf(%p, %n) vararg(!llvm.func<i32 (!llvm.ptr, i32, ...)>) : (!llvm.ptr, i32) -> i32`(见 §2.9) |
|
||||
|
||||
### 2.3 常量必须物化为 op(关键)
|
||||
|
||||
@@ -162,16 +181,29 @@ MLIR 的操作数不能是字面量。**扫描阶段**(pass 1)遍历函数
|
||||
|----------|-----------|
|
||||
| 字符串常量(`c"hello\00"`,`[6 x i8]`) | `llvm.mlir.global private unnamed_addr constant @".str"("hello\00") : !llvm.array<6 x i8>` |
|
||||
| 整数全局(`0`,`i32`) | `llvm.mlir.global @counter(0 : i32) : i32` |
|
||||
| stub 模式(无初值) | `llvm.mlir.global external @x : i32` |
|
||||
| stub 模式(无初值) | `llvm.mlir.global external @x() : i32`(**值括号必须保留**,实测见下) |
|
||||
| linkage / constant / unnamed_addr | 对应关键字(`private`/`internal`/`linkonce_odr`、`constant`、`unnamed_addr`) |
|
||||
| section / align | 属性字典 `{section = "…", alignment = N : i64}`(**形态未经 mlir-opt 验证**) |
|
||||
| section / align | 属性字典 `{section = "…", alignment = N : i64}`,**位置在值括号之后、类型之前**(已实测,见下) |
|
||||
| 整型/浮点数组 `[i8 1, i8 2]` | `[1 : i8, 2 : i8]`(元素**带类型注解**;`dense<…>` 对 `!llvm.array` **非法**) |
|
||||
| `null` / `zeroinitializer` | `zeroinitializer` |
|
||||
| 根级裸 `null` / `zeroinitializer` | **initializer region**:`@p() : !llvm.ptr` + `%v0 = llvm.mlir.zero : <顶层类型>` + `llvm.return`(属性形态 `(zeroinitializer)` 被 mlir-opt 拒绝:`expected attribute value`,实测 22.1.8) |
|
||||
| 结构体 / vtable `{ i8* null, i8* bitcast (… @m0 …) }` | **initializer region**:`@vt()` + `: !llvm.struct<(…)>` + `{ <叶子 op>; undef; insertvalue 链; llvm.return }` |
|
||||
| 指针数组 + GEP `[i8* getelementptr … @s0 …]` | region;GEP/bitcast 常量表达式整体等价 `llvm.mlir.addressof @s0 : !llvm.ptr` |
|
||||
| 指针型初值 `i8* @s0` / `i8* null` | region(顶层叶子:无 undef/insertvalue,直接 `llvm.return`) |
|
||||
|
||||
**初值两条路径**(判定见 `__module.vp:_MlirGlobalInitNeedsRegion`):
|
||||
`c"…"` / `"…"` / 标量字面量 / **纯标量数组**走**属性路径**;
|
||||
根级裸 `null` / `zeroinitializer`、结构体 `{…}`、含符号引用(`@`)或结构体的数组、
|
||||
顶层 `@name`、指针/聚合类型前缀开头的初值
|
||||
(`i8* null`、`i8* bitcast (…)`、`i8* getelementptr …`)走 **region 路径**。
|
||||
region 内元素 MLIR 类型并行取自**全局类型树**(`Struct` 字段链 / `Array` 元素类型),
|
||||
下标为多下标路径(嵌套聚合如 `[0, 1]`),`insertvalue`/`llvm.return` 的类型后缀始终是**顶层聚合类型**。
|
||||
符号名一律走 `_MlirSymRefAppend`(先剥 LLVM 口径引号,再按 MLIR 口径重加)。
|
||||
两条路径都覆盖不到的叶子形态一律 `MlirFail`(带原始文本)。
|
||||
|
||||
- `module_set_target` / `module_set_datalayout`:MLIR 模块不携带 triple/datalayout
|
||||
⇒ **记录但不输出**,模块输出里给一行注释标明"由 mlir-translate / llc 阶段提供"。
|
||||
|
||||
**⚠ 三条由 mlir-opt 22.1.8 实测确立的硬约束(违反即解析/校验失败)**
|
||||
**⚠ 五条由 mlir-opt 22.1.8 实测确立的硬约束(违反即解析/校验失败)**
|
||||
|
||||
1. **符号名判据与 LLVM 不同**:MLIR 裸标识符**首字符必须是字母或 `_`**,且
|
||||
**不允许 `-`**(LLVM 允许)。⇒ TPV 的 `{sha1}.{name}`(sha1 以数字开头)与
|
||||
@@ -183,6 +215,15 @@ MLIR 的操作数不能是字面量。**扫描阶段**(pass 1)遍历函数
|
||||
3. **`dense<[…] : !llvm.array<…>>` 非法**(`dense` 只接受 builtin shaped 类型
|
||||
tensor/vector);数组属性必须是 `值 : 类型` 的元素列表形态,且元素类型与
|
||||
全局类型一致(元素不注解时按 i64 解释)。
|
||||
4. **属性字典必须在类型之前**(紧跟值括号):正确形态
|
||||
`llvm.mlir.global internal constant @s("ab\00") {section = "data", alignment = 16 : i64} : !llvm.array<3 x i8>`
|
||||
exit 0;把字典放到**类型之后**(`… : !llvm.array<3 x i8> {section = "data"}`)
|
||||
会把 `section` 当成新 op,报 ``Dialect `' not found for custom op 'section'``。
|
||||
同理 `unnamed_addr` 是**关键字**(输出在名字之前),**不能**放进字典 ——
|
||||
放进字典报 `attribute 'unnamed_addr' occurs more than once`。
|
||||
5. **值括号必须存在**:stub 的 `external` 声明没有初值,但仍要写空括号 ——
|
||||
`llvm.mlir.global external @x() : i32` exit 0;
|
||||
`llvm.mlir.global external @x : i32` 报 `expected '('`。
|
||||
|
||||
**⚠ 验收口径**:类型文本、值文本、**裸函数**都不是合法的 MLIR 顶层单元
|
||||
(类型/值不是顶层 op;裸函数父级不是 SymbolTable,其外部符号引用无法解析)
|
||||
@@ -207,22 +248,242 @@ module {
|
||||
`ValuePrint`:SSA → `%0`;常量 → `42 : i32`(**仅供诊断**)。
|
||||
指令渲染**不依赖** `ValuePrint`,`FunctionPrint` 用自己的操作数解析。
|
||||
|
||||
### 2.7 alloca 对齐(`build_alloca_aligned`)
|
||||
|
||||
中间文本 `%N = alloca <ty>, align N` → `%N = llvm.alloca %c0 x i32 {alignment = N : i64} : (i32) -> !llvm.ptr`
|
||||
|
||||
- 属性形态**由 mlir-opt 22.1.8 实测确定**(不得凭记忆):
|
||||
`{alignment = 16 : i64}` exit 0;`align(16)` / `, align 16`(LLVM 词法)报 `expected ':'`;
|
||||
`{alignment = 16 : i32}` 报 `attribute 'alignment' failed to satisfy constraint:
|
||||
64-bit signless integer attribute`(必须是 **i64**)。
|
||||
- 对齐值只接受**常量十进制 + 2 的幂**:非常量(SSA)/非幂/超范围(> 2^30)一律 `MlirFail`
|
||||
—— mlir-opt 自身不校验幂次(实测 `{alignment = 3 : i64}` exit 0),故由本层把关。
|
||||
|
||||
### 2.8 内联汇编(`build_inline_asm` / TPV 注入文本)
|
||||
|
||||
两条来源的中间文本 opcode 都是 `call`,由 `_MlirCall` 转交 `_MlirAsm`:
|
||||
|
||||
| 中间文本(LLVM 词法) | MLIR 文本 |
|
||||
|---|---|
|
||||
| `call none asm sideeffect "nop", ""()` | `llvm.inline_asm has_side_effects "nop", "" : () -> ()` |
|
||||
| `%N = call i32 asm sideeffect "…", "=r,r"()` | `%N = llvm.inline_asm has_side_effects "…", "=r,r" : () -> i32` |
|
||||
| `%N = call i32 asm sideeffect inteldialect "…", "=r,r"(i32 %v)` | `%N = llvm.inline_asm has_side_effects asm_dialect = intel "…", "=r,r" %v : (i32) -> i32` |
|
||||
| `%N = call {i32, i32} asm sideeffect "…", "=r,r"()` | `%N = llvm.inline_asm has_side_effects "…", "=r,r" : () -> !llvm.struct<(i32, i32)>`(**多结果 = 单结果 + 结构体类型**) |
|
||||
|
||||
- 方言关键字:LLVM 的 `inteldialect` → MLIR 的 **`asm_dialect = intel`**
|
||||
(裸 `inteldialect` 实测报 `expected attribute value`;
|
||||
`asm_dialect = inteldialect` 报 `expected … enum values … [att, intel]`)。
|
||||
- **输入操作数在 MLIR 里是逗号分隔的裸操作数、不带括号**(LLVM 词法的 `"…"(%v)`
|
||||
实测报 `expected ':'`);操作数类型串位于 `: (<操作数类型…>) -> <返回类型|()>`。
|
||||
- 汇编/约束串按引号串整体透传:TPV 的 `\22`/`\5C`/`\0A` 转义对 MLIR 词法同样合法(实测 exit 0)。
|
||||
- 只认 `sideeffect` / `inteldialect` 两个关键字;其它记号(含 LLVM 的
|
||||
`attdialect`/`alignstack`/`unwind`)、缺约束串、实参不成对、有返回值却无结果名
|
||||
⇒ `MlirFail` 响亮报错。
|
||||
(`attdialect`/`alignstack` 在 1024 份真实 `.ll` 语料中 **0 命中** ⇒ **不列入支持计划**,
|
||||
此处的 `MlirFail` 只是兜底,不是待补齐缺口。)
|
||||
|
||||
**多结果内联汇编 + `extractvalue`**(本轮补齐;TPV 全量产物 4163 份 `.ll` 的
|
||||
"指令位置"扫描中 `extractvalue` **70 处**,且是**唯一**未支持却真实使用的 opcode)
|
||||
|
||||
真实形态(`Test/DefaultArgTest/output/*.ll` 等,多结果内联汇编是 `extractvalue`
|
||||
的**唯一来源**——`rdrand`/`rdseed` 类 builtin):
|
||||
|
||||
```
|
||||
%asm_result = call {i32, i32} asm inteldialect "rdrand $1", "=r,=r,~{cc}"()
|
||||
%asm_out_0 = extractvalue {i32, i32} %asm_result, 0
|
||||
%asm_out_1 = extractvalue {i32, i32} %asm_result, 1
|
||||
```
|
||||
|
||||
(语料实测结构体结果有 `{i32, i32}` / `{i64, i64}` / `{i32, i32, i32, i32}` /
|
||||
`{i64, i32, i64, i32}`,索引一律**单级**。)
|
||||
|
||||
| 中间文本(LLVM 词法) | MLIR 文本 |
|
||||
|---|---|
|
||||
| `%N = call {i32, i32} asm sideeffect "…", "=r,r"()` | `%N = llvm.inline_asm has_side_effects "…", "=r,r" : () -> !llvm.struct<(i32, i32)>` |
|
||||
| `%r = extractvalue {i32, i32} %N, 0` | `%r = llvm.extractvalue %N[0] : !llvm.struct<(i32, i32)>` |
|
||||
| `%r = extractvalue {i32, {i32, i32}} %N, 1, 0` | `%r = llvm.extractvalue %N[1, 0] : !llvm.struct<(i32, !llvm.struct<(i32, i32)>)>` |
|
||||
|
||||
- **多结果 = "单结果 + 结构体返回类型"**:`_MlirAsm` **无需特殊分支**(返回类型
|
||||
文本经 `MlirTypeText` 即 `{…}` → `!llvm.struct<(...)>`,嵌套结构体亦同)。
|
||||
- `extractvalue` 由 `_MlirExtractValue` 渲染(`_MlirRenderLine` 分派):聚合类型
|
||||
文本在**值之前**(LLVM 词法)→ `MlirTypeText` 转 MLIR 类型;**索引必须从 LLVM 的
|
||||
`<聚合>, i0, i1` 转写为 MLIR 的 `[i0, i1]`**(MLIR 实测拒绝 LLVM 词法:把 `<聚合>`
|
||||
当结果类型、`%v,` 当多余记号)。
|
||||
- 边界:SSA 索引 / 缺索引 / 索引后有多余记号 ⇒ `MlirFail` 响亮报错。
|
||||
**多级索引**(`%v[1, 0]`)经 mlir-opt 22.1.8 实测**合法** ⇒ 一并支持。
|
||||
- 实测(mlir-opt / mlir-translate 22.1.8):`%v[0]` / `%v[1, 0]` / 具名结构体
|
||||
`!llvm.struct<"n", (i32, i32)>` / 数组 `!llvm.array<2 x i32>` 作聚合类型均 exit 0;
|
||||
降级得 `call { i32, i32 } asm sideeffect "…", "=r,r"()` +
|
||||
`extractvalue { i32, i32 } %1, 0` / `, 1`(与 TPV 产物逐字同形)。
|
||||
- `insertvalue` / `invoke` / `landingpad` / `resume` / `freeze` / `fence` /
|
||||
`shufflevector` / `insertelement` / `extractelement` / `callbr` 在 4163 份真实产物中
|
||||
**全部 0 命中** ⇒ **不实现**,仍走"未知指令 opcode"响亮报错。
|
||||
|
||||
### 2.9 变参(`...`)/ `va_arg` / `cmpxchg`
|
||||
|
||||
三者形态均由 mlir-opt 22.1.8 实测 exit 0 确立(候选与 exit 码见
|
||||
`__function.vp` 的 `_MlirFuncSig` / `_MlirCall` / `_MlirVaArg` / `_MlirCmpXchg`);
|
||||
选做依据是真实 `.ll` 语料频次(变参 call 8831 行/627 文件、`va_arg` 1994/397、
|
||||
`cmpxchg` 547/371)。
|
||||
|
||||
**① 变参函数声明**(`func.IsVarArg == 1`):参数表尾追加 `...`
|
||||
(有固定参数 ⇒ `, ...`,否则裸 `...`)。
|
||||
|
||||
**② 变参调用**:中间文本 `call <ret> (<实参类型…>, ...) @f(<实参>)`
|
||||
(builders 的 `build_call(is_variadic=1)`)在操作数括号后追加
|
||||
`vararg(<被调函数的完整 MLIR 函数类型>)`:
|
||||
|
||||
```
|
||||
%0 = llvm.call @printf(%p, %n) vararg(!llvm.func<i32 (!llvm.ptr, i32, ...)>) : (!llvm.ptr, i32) -> i32
|
||||
```
|
||||
|
||||
- `vararg(...)` 内是**完整函数类型**(返回类型 + 中间文本括号里的参数类型串,含 `...`);
|
||||
- **void 返回时该处返回类型写 `void`** —— 写 `none` 实测报
|
||||
`expected var_callee_type to return void`(普通 `-> ()` 位置仍用 `()`);
|
||||
- 降级验证:`mlir-translate --mlir-to-llvmir` 得
|
||||
`%3 = call i32 (ptr, ...) @printf(ptr %0, i32 %1)` ✓。
|
||||
|
||||
**③ `va_arg` —— 用 generic(通用 op)形态是刻意选择**:
|
||||
|
||||
```
|
||||
%0 = "llvm.va_arg"(%ap) : (!llvm.ptr) -> i32
|
||||
```
|
||||
|
||||
- 自定义汇编语法 `llvm.va_arg %ap, i32` 要求显式给出 va_list 的类型(LLVM 里的
|
||||
`ptr`),而 22.x **不透明指针**下该类型无法表达 ⇒ mlir-opt 报
|
||||
`expected builtin.function`。故**刻意改用通用 op 语法**,只用操作数/结果类型列表描述。
|
||||
- 降级验证:`mlir-translate --mlir-to-llvmir` 得 `%2 = va_arg ptr %0, i32` ✓
|
||||
(与 LLVM 的 va_arg 完全同形)。
|
||||
|
||||
**④ `cmpxchg` —— MLIR 里是单结果 `struct{T, i1}`,不是两个 SSA 值**:
|
||||
中间文本两态(builder 的 `%N = cmpxchg …` 与 `%a, %b = cmpxchg …`)都渲染为
|
||||
**一条 op + 两条 `extractvalue`**:
|
||||
|
||||
```
|
||||
%N = llvm.cmpxchg %p, %c, %n acq_rel monotonic : !llvm.ptr, i32
|
||||
%_N_v = llvm.extractvalue %N[0] : !llvm.struct<(i32, i1)> ; [0] = 旧值(比较值)
|
||||
%_N_ok = llvm.extractvalue %N[1] : !llvm.struct<(i32, i1)> ; [1] = i1 成功位
|
||||
```
|
||||
|
||||
- 结果名绑定:`%N`(单结果)/ `%a`(双结果首名)→ 旧值 `[0]`;双结果次名 `%b` → 成功位 `[1]`。
|
||||
绑定复用 bitcast no-op 的**别名设施**(`_MlirAliasAdd`),后续引用经 `_MlirResolve`
|
||||
自动改写为上述两条 `extractvalue` 的结果名 ⇒ builder 端 `build_cmpxchg` 返回的
|
||||
「T 类型 Value」(旧值)语义保持。
|
||||
- 两条 `extractvalue` 结果名由 struct 结果名**确定性派生**(`_MlirCxResName`):
|
||||
`%0` → `%_0_v`(旧值)/ `%_0_ok`(成功位),`%a` → `%_a_v`/`%_a_ok`。
|
||||
`_` 前缀是必需的——MLIR 裸 SSA 名首字符须为字母或 `_`,`%0_v` 被 mlir-opt 22.1.8
|
||||
拒绝(`expected '=' after SSA name`)。名字与结果名一一对应、**不含计数器**
|
||||
⇒ pass1(登记别名)与 pass2(发射)必然同名,引用不会悬空。
|
||||
取 `[0]`(旧值)在前、`[1]`(成功位)在后。
|
||||
- 降级验证:`mlir-translate --mlir-to-llvmir` 得
|
||||
`cmpxchg ptr %0, i32 %1, i32 %2 acq_rel monotonic, align 4` +
|
||||
`extractvalue { i32, i1 } %4, 0` / `, 1` ✓。
|
||||
|
||||
### 2.10 具名结构体(`%"name" = type {…}`)
|
||||
|
||||
**背景(实测语料)**:TPV 对每个类都调 `module_add_named_type`(18 处调用点:
|
||||
`ClassGenDef.vp:522`、`ClassGenEnum.vp:720/1000`、`StructGenMixin.vp:1802`、
|
||||
`AsyncGenMixin.vp:733`、`VpiReader.vp:2705/4059` 等),llvmlite 侧由
|
||||
`PrintStructDefinition` 输出 `%"sha1.Cls" = type {…}` 定义行
|
||||
(`.tpv_build/**/*.ll` 里 `^%"[^"]+" = type` 命中 78319 行 / 1815 文件)。
|
||||
|
||||
**MLIR 侧两条硬约束(实测 mlir-opt 22.1.8)**:
|
||||
|
||||
1. **无独立类型定义语法** ⇒ 定义行**整体丢弃**(`PrintStructDefinition` 输出空串,
|
||||
`PrintModule` 完全不遍历 `NamedTypeHead` 输出文本);
|
||||
2. **不允许无体前向声明** ⇒ `!llvm.struct<"x">`(只有名字、无字段表)报
|
||||
`struct without a body only allowed in a recursive struct`;opaque
|
||||
(`fcount < 0` 哨兵)**无法表达**。
|
||||
|
||||
**唯一可表达形态 = 使用处的内联具名体**(四种均实测 exit 0):
|
||||
|
||||
| 场景 | MLIR 文本 |
|
||||
|------|-----------|
|
||||
| 按值(形参 / 返回 / 全局 / 字段 / alloca 元素) | `!llvm.struct<"aa11bb22cc33dd44.Cls", (i32, !llvm.ptr)>` |
|
||||
| 名字含数字 / `.` / `[` / `]`(泛型特化名) | `!llvm.struct<"01c70390f01048df.Lock[0]", (i32)>`(**直接作字符串字面量,不额外转义/加引号**) |
|
||||
| 嵌套具名(字段是另一个具名结构体) | `!llvm.struct<"A.Lock", (!llvm.struct<"A.CRIT", (i32)>)>` |
|
||||
| 空体(无字段的类) | `!llvm.struct<"x.Empty", ()>` |
|
||||
| packed(`@c.Attribute(t.attr.packed)`) | `!llvm.struct<"a.P", packed (i32, i8)>`(关键字在字段表**前**;降级 `%a.P = type <{ i32, i8 }>`;空体 `packed ()` 亦 exit 0) |
|
||||
| `Ptr(具名结构体)`(`%"name"*`) | `!llvm.ptr`(不透明指针,既有路径**不变**) |
|
||||
|
||||
**定义行由 MLIR 自动生成(实测降级证据)**:`mlir-translate --mlir-to-llvmir`
|
||||
会把使用处的内联形态合成为定义行,故模块里**不需要**(也不能)手写定义行:
|
||||
|
||||
```
|
||||
%aa11bb22cc33dd44.Cls = type { i32, ptr } ; ← 模块文本里没有这一行
|
||||
%x.Empty = type {}
|
||||
%"01c70390f01048df.Lock[0]" = type { i32 }
|
||||
@per_cpu = internal global %aa11bb22cc33dd44.Cls { i32 undef, ptr null }
|
||||
```
|
||||
|
||||
**实现位点**:
|
||||
|
||||
- `__types.vp:_MlirNamedStructAppend` —— 具名内联体渲染(`TypePrint` /
|
||||
`_MlirTypePrintDepth` 的具名分支;匿名分支 `_MlirStructFields` 不变);
|
||||
`packed` 非 0 时在字段表**前**多输出 `packed `;
|
||||
- `__types.vp:_MlirNamedStructText` —— 中间文本 `%"name"` → 查登记表展开
|
||||
(packed 标记与类型同槽取出,故文本路径同样输出 `packed (…)`);
|
||||
- `__module.vp:_MlirPublishNamedTypes` —— `PrintModule` 开始时按 `NamedTypeHead`
|
||||
发布登记表(`MlirNamedTypesBegin(pool, cnt)` +
|
||||
`MlirNamedTypeRegister(ty, is_packed)`,`cnt` 取自链表实际长度,`is_packed`
|
||||
取自 `NamedTypeNode.IsPacked`),打印结束时 `MlirNamedTypesClear`(表内类型
|
||||
指针只在当次打印期间有效)。
|
||||
|
||||
**响亮报错(绝不静默输出错误布局)**:
|
||||
|
||||
| 情形 | 报文 |
|
||||
|------|------|
|
||||
| 打印时该具名结构体仍是 opaque(`fcount < 0`,跨模块前向声明未被完整定义替换) | `命名结构体在打印时仍为 opaque(无字段体)——MLIR 不允许无体前向声明(类型名 '…')` |
|
||||
| 具名但字段链缺失(`FCount` 非 0 而 `Fields == None`) | `命名结构体字段链缺失(FCount 非 0 但无字段)` |
|
||||
| 名字含 `"` / `\` / 控制字符 | `命名结构体名字含 MLIR 字符串字面量非法字符` |
|
||||
| 名字为空 / 字段链成环(> 4096) | `命名结构体名字为空` / `命名结构体字段过多或字段链成环` |
|
||||
| 中间文本 `%"name"` 未登记(含"表未发布"的独立 `FunctionPrint`) | `命名结构体类型文本未在模块中登记完整定义(MLIR 不允许无体前向声明…)` |
|
||||
|
||||
**opaque 风险查证(结论:按值使用处不会出现 opaque)**:TPV 的 opaque 只由
|
||||
`_scan_type_for_cross_module_refs`(`Ptr(Struct(name))` 分支)与 REPL 专用的
|
||||
`_repl_opaque_class_ty` 产生,两者都服务于**指针**引用;按值位置(C ABI 值形参)
|
||||
另有显式判据 `_AggregateParamTy`:命中注册表且非 opaque 才按值、否则**降级 i64**;
|
||||
值类型的全局 / 返回类型先经 `_ensure_full_value_types` 注册完整定义。故打印时
|
||||
按值出现的具名结构体字段都是完整的;一旦不完整即按上表响亮报错(旧行为是静默
|
||||
输出空体 `!llvm.struct<()>` ⇒ 错误布局)。
|
||||
|
||||
**packed 结构体(已接入)**:`@c.Attribute(t.attr.packed)` →
|
||||
`module_set_named_type_packed` → `NamedTypeNode.IsPacked`(**唯一设置点**)。
|
||||
packed 标记只挂在**具名**节点上(`LLVMType.Struct` 变体没有该字段;
|
||||
`module_set_named_type_packed` 对 `Name == None` 直接返回),故匿名结构体
|
||||
(`{…}` → `!llvm.struct<(...)>`)**结构上无法**携带 packed —— 不存在"无名却要求
|
||||
packed"的组合,无需(也无处)为其单独报错。登记表每槽 16 字节
|
||||
(`{ LLVMType*, IsPacked }`),打印期由 `_MlirPublishNamedTypes` 从节点读出;
|
||||
渲染侧(`TypePrint` 具名分支经 `_MlirNamedPackedOf` 回查;文本路径
|
||||
`_MlirNamedStructText` 与类型同槽取出)据标记在字段表前输出 `packed `。
|
||||
实测 mlir-opt / mlir-translate 22.1.8 exit 0:
|
||||
`!llvm.struct<"a.P", packed (i32, i8)>` → `%a.P = type <{ i32, i8 }>`;
|
||||
空体 `!llvm.struct<"x.Empty", packed ()>` → `%x.Empty = type <{}>`。
|
||||
|
||||
---
|
||||
|
||||
## 3. v1 支持 / 不支持清单
|
||||
|
||||
### 3.1 支持(已实现)
|
||||
|
||||
类型:`iN` 全整数、`f16/f32/f64/f128`、不透明指针、数组、结构体、函数类型、`Void`。
|
||||
类型:`iN` 全整数、`f16/f32/f64/f128`、不透明指针、数组、结构体(含**具名结构体**
|
||||
`!llvm.struct<"name", (…)>` 与 **packed 具名结构体** `!llvm.struct<"name", packed (…)>`,
|
||||
见 §2.10)、函数类型、`Void`。
|
||||
|
||||
指令:`add/sub/mul/sdiv/udiv/srem/urem/and/or/xor/shl/lshr/ashr`、
|
||||
`fadd/fsub/fmul/fdiv/frem/fneg`、`icmp/fcmp`、`alloca`、`load/store`(含 `volatile`)、
|
||||
`fadd/fsub/fmul/fdiv/frem/fneg`、`icmp/fcmp`、`alloca`(含 `, align N` 对齐属性,见 §2.7)、
|
||||
`load/store`(含 `volatile`)、
|
||||
`getelementptr`(单索引 / 数组 / 结构体)、`bitcast`(ptr→ptr 折叠为 no-op)、
|
||||
`sext/trunc/zext/ptrtoint/inttoptr/fpext/fptrunc/fptosi/sitofp/fptoui/uitofp`、
|
||||
`call`(直接 / 间接)、`br/cond_br`(含块实参)、`ret/ret void`、`unreachable`、
|
||||
`select`、`phi`(→ 块参数)、`switch`、`atomicrmw`。
|
||||
`call`(直接 / 间接 / **变参**)、`inline_asm`(`sideeffect` / `inteldialect`,**含多结果**,见 §2.8)、
|
||||
`br/cond_br`(含块实参)、`ret/ret void`、`unreachable`、
|
||||
`select`、`phi`(→ 块参数)、`switch`(含目标块带 phi 的块实参)、`atomicrmw`、
|
||||
**`va_arg`**、**`cmpxchg`**(三者见 §2.9)、**`extractvalue`**(多结果内联汇编的结果抽取,
|
||||
含多级索引,见 §2.8);**变参函数声明**(`llvm.func @f(…, ...)`)。
|
||||
|
||||
全局初值:整数、浮点、`null`、`zeroinitializer`、cstring、整型数组(含嵌套数组)。
|
||||
全局初值:整数、浮点、cstring、整型数组(含嵌套数组)走**属性路径**;
|
||||
根级裸 `null` / `zeroinitializer`、结构体/vtable、含符号引用的聚合、指针型初值
|
||||
走 **initializer region**(叶子 op + undef + insertvalue 链 + return;见 §2.5)。
|
||||
|
||||
### 3.2 不支持 —— 一律**响亮报错**(`MlirFail`:打印定位信息后 `exit(1)`)
|
||||
|
||||
@@ -230,19 +491,25 @@ module {
|
||||
|------|------------------|------|
|
||||
| `Label` 类型 | `TypePrint` / `_MlirTypePrintDepth` | `TypePrint(Label): MLIR 无 label 类型,v1 不支持` |
|
||||
| `Metadata` 类型 | `TypePrint` / `_MlirTypePrintDepth` | `TypePrint(Metadata): MLIR llvm 方言 v1 不支持 metadata 类型` |
|
||||
| `alloca …, align N` | `_MlirAlloca` | `alloca 对齐(build_alloca_aligned)v1 不支持` |
|
||||
| 变参函数 `...` | `_MlirFuncSig` | `变参函数(...)的 llvm.func 形态未固化,v1 不支持` |
|
||||
| 变参 `call`(builder 的 `call <ret> (<ftys>) @f(...)`) | `_MlirCall` | `变参 call 的 MLIR 形态未固化,v1 不支持` |
|
||||
| `cmpxchg` | `_MlirRenderLine` | `cmpxchg 的 MLIR 形态未固化,v1 不支持` |
|
||||
| `va_arg` | `_MlirRenderLine` | `va_arg v1 不支持` |
|
||||
| 内联汇编(中间文本为 `call <ret> asm …`) | `_MlirCall` | `inline_asm 的 MLIR 形态未固化,v1 不支持` |
|
||||
| `{ … }` 结构体 / vtable 全局初始化串 | `_MlirGlobalInit` | `全局初值形态 v1 不支持(结构体/vtable 初始化串需改用结构化初始化)` |
|
||||
| `alloca` 对齐值非常量/非 2 的幂/超范围(> 2^30) | `_MlirAlloca` | `alloca 对齐值必须是常量(常量十进制,非 SSA)` / `alloca 对齐值必须是 2 的幂` / `alloca 对齐值超范围(须为 2 的幂且 <= 2^30)` |
|
||||
| `alloca` 的 `, <非 align 属性>` | `_MlirAlloca` | `alloca 只支持 align 属性(未知属性)` |
|
||||
| `inline_asm` 的未知关键字(含 LLVM 的 `attdialect`/`alignstack`/`unwind`)或非字符串汇编文本 | `_MlirAsm` | `inline_asm 缺少汇编文本(或含未知关键字)` |
|
||||
| `inline_asm` 缺约束串 / 约束串非字符串 / 缺实参列表 / 实参后有多余记号 | `_MlirAsm` | `inline_asm 缺少约束串(形态须为 "asm", "constraints")` 等 |
|
||||
| `inline_asm` 有返回值却无结果名(中间文本缺 `%N = `) | `_MlirAsm` | `inline_asm 有返回值但缺少结果名(%N = call …)` |
|
||||
| 双结果前缀 `%a, %b =` 用于非 `cmpxchg`,或缺 `=` | `_MlirRenderLine` | `双结果前缀(%a, %b =)只支持 cmpxchg` / `双结果前缀(%a, %b =)缺少 '='` |
|
||||
| `cmpxchg` 缺结果名 / extractvalue 结果名分配失败 | `_MlirCmpXchg` / `_MlirCxResName` | `cmpxchg 缺少结果名` / `cmpxchg 结果名分配失败` |
|
||||
| `va_arg` 缺结果名 | `_MlirVaArg` | `va_arg 缺少结果名` |
|
||||
| `extractvalue` 缺索引 / SSA 索引 / 索引后有多余记号 / 索引过多(> 32) | `_MlirExtractValue` | `extractvalue 缺少索引` / `extractvalue 索引必须是常量(SSA 索引不支持)` / `extractvalue 索引之后出现多余记号` / `extractvalue 索引过多` |
|
||||
| 变参 `call` 的函数类型参数过多(> 256) | `_MlirCall` | `变参 call 的函数类型参数过多` |
|
||||
| 全局初值叶子形态非 `符号引用`/`null`/`zeroinitializer`/`标量字面量`(如聚合里的字符串常量) | `_MlirGvWalkLeaf` | `全局初值叶子形态 v1 不支持:'<原始文本>'` |
|
||||
| 初值树与全局类型树不一致(初值为结构体/数组但全局类型不匹配) | `_MlirGvWalkStruct` / `_MlirGvWalkArray` | `初值为结构体,但全局类型不是结构体` / `初值为数组,但全局类型不是数组` |
|
||||
| 初值嵌套超过 64 层 | `_MlirGvWalk` | `全局初值嵌套过深(> 64 层)` |
|
||||
| phi 不在块首(非连续) | `_MlirRenderLine` | `phi 不在块首(v1 只支持块首连续 phi → 块参数)` |
|
||||
| entry 块出现 phi | `_MlirCheckEntryNoPhi` | `entry 块不允许 phi(MLIR entry 块不接受块参数)` |
|
||||
| 跳转目标带 phi 但缺匹配入边 | `_MlirBlockOperands` | `跳转目标块带 phi,但找不到与源块匹配的入边` |
|
||||
| `switch` 目标块带 phi | `_MlirSwitch` | `switch 目标块带 phi(携带块实参)v1 不支持` |
|
||||
| 跳转目标(含 `switch` 的 case/default 目标)带 phi 但缺匹配入边 | `_MlirBlockOperands` | `跳转目标块带 phi,但找不到与源块匹配的入边` |
|
||||
| `switch` case 值是 SSA | `_MlirSwitch` | `switch case 值必须是常量(SSA 值不支持)` |
|
||||
| `%name` 命名结构体类型文本(中间文本里不应出现) | `_MlirNamedStructText` | `命名结构体类型文本 v1 不支持(应使用内联 !llvm.struct<(…)>)` |
|
||||
| 具名结构体在打印时仍为 opaque / 字段链缺失;名字含 `"`、`\`、控制字符;字段链成环 | `_MlirNamedStructAppend` / `_MlirNamedCheckName` | 见 §2.10 报错表 |
|
||||
| 中间文本 `%"name"` 未登记完整定义(含独立 `FunctionPrint` 时登记表未发布) | `_MlirNamedStructText` | `命名结构体类型文本未在模块中登记完整定义(MLIR 不允许无体前向声明…)` |
|
||||
| 中间文本里出现未知 opcode | `_MlirRenderLine` | `未知指令 opcode` |
|
||||
| 无法转换的类型文本 | `_MlirTypeAppend` | `无法转换为 MLIR 类型文本(v1 未覆盖该形态)` |
|
||||
| 常量操作数形态非 整数/浮点/null/符号 | `_MlirEmitOneConst` | `不支持的常量操作数形态` |
|
||||
@@ -256,8 +523,11 @@ module {
|
||||
v1 **记录但不渲染**(模型字段保留,`function_set_attrs` / `function_set_attr_group` 同名可用)。
|
||||
- `Param.Attrs`(`noundef`/`nocapture` 等):同上。
|
||||
- `NamedTypeHead` 类型定义链表:MLIR 无独立类型定义语法,结构体在使用处内联为
|
||||
`!llvm.struct<(…)>`;`module_add_named_type` 保留模型语义(去重/opaque 替换)但
|
||||
不再产生输出文本,`PrintStructDefinition` 输出空串(保留 API 同形)。
|
||||
`!llvm.struct<"name", (…)>`(具名)/ `!llvm.struct<(…)>`(匿名),packed 具名
|
||||
再加关键字(`!llvm.struct<"name", packed (…)>`);**不输出**任何
|
||||
类型定义行。`module_add_named_type` 保留模型语义(去重 / opaque 就地替换)并作为
|
||||
打印期登记表的来源(`_MlirPublishNamedTypes`,见 §2.10),`PrintStructDefinition`
|
||||
输出空串(保留 API 同形)。
|
||||
|
||||
---
|
||||
|
||||
@@ -265,34 +535,44 @@ module {
|
||||
|
||||
| # | 同形点 | 语义差异 |
|
||||
|---|--------|----------|
|
||||
| 1 | `TypePrint(buf,size,ty,pool)` | 输出 MLIR 类型而非 LLVM 类型;`Ptr(_)` 恒为 `!llvm.ptr`(丢失 pointee),`Void` 为 `none`,`Label` 报错 |
|
||||
| 1 | `TypePrint(buf,size,ty,pool)` | 输出 MLIR 类型而非 LLVM 类型;`Ptr(_)` 恒为 `!llvm.ptr`(丢失 pointee)、`Struct(…,name)` 内联为 `!llvm.struct<"name", (…)>`(丢失"定义行"这一概念,见 §2.10),`Void` 为 `none`,`Label` 报错 |
|
||||
| 2 | `ValuePrint(buf,size,val,pool)` | SSA 只输出 `%0`(不带类型);常量输出 `42 : i32`;llvmlite 版是 `i32 42` |
|
||||
| 3 | `FunctionPrint(buf,size,func,pool)` | 输出 `llvm.func`/`llvm.*` op;**入口常量物化**、**phi→块参数**、**no-op bitcast 折叠** |
|
||||
| 4 | `LLVMModulePrint(buf,size,mod,pool,mode)` | 输出 `module { … }`;`OUTPUT_STUB/TEXT` 只能用注释标示(MLIR 无此二分);triple/datalayout 不输出 |
|
||||
| 5 | `IRBuilder.build_bitcast(val,to_ty)` | 返回的 `Value` 名字仍会出现在中间文本里,但打印时若两端都是指针则**整个指令消失**、名字被别名替换(`Value` 对象本身不变) |
|
||||
| 6 | `IRBuilder.build_phi` / `build_br` | builder 语义不变(仍写 phi 行);MLIR 打印时 phi 行消失、br 追加实参 |
|
||||
| 7 | `IRBuilder.build_alloca` | 打印时个数按常量 `1`(i32)物化;`build_alloca_aligned` 报错 |
|
||||
| 7 | `IRBuilder.build_alloca` | 打印时个数按常量 `1`(i32)物化;`build_alloca_aligned` 的 `, align N` 转写为 `{alignment = N : i64}` 属性(N 必须是 2 的幂常量,否则响亮报错) |
|
||||
| 8 | `const_int` / `ConstFloat` | `Name` 文本仍是 llvmlite 形态(十进制 / `0x` 位模式);**转换发生在打印阶段**(浮点位模式→十进制科学计数法) |
|
||||
| 9 | `create_global_string` | `Initializer` 仍是 `c"…\00"`;打印时转成 `"…"`(**去掉尾随 `\00`**,见 §5 待确认) |
|
||||
| 10 | `verify_function` / `verify_module` | 校验内部模型 + 中间文本(与 llvmlite 完全一致);**不**校验 MLIR 文本合法性(交给 mlir-opt) |
|
||||
| 11 | `PrintStructDefinition` | 输出空串(MLIR 无类型定义行),llvmlite 版输出 `%"name" = type {…}` |
|
||||
| 11 | `PrintStructDefinition` | 输出空串(MLIR 无类型定义行),llvmlite 版输出 `%"name" = type {…}`;具名结构体只在**使用处**内联出现(§2.10) |
|
||||
| 12 | `G_PeepEnable` | 语义不变:窥孔作用在**中间文本**(`ret zeroinitializer→0/null`、入口槽 store→load 转发、no-op bitcast 消除),与 MLIR 渲染正交(T3 与 MLIR 的 no-op 折叠叠加,结果一致) |
|
||||
| 13 | `module_set_named_type_packed` | 语义不变(记录 packed);MLIR 侧 packed 布局未参与渲染(字段列表渲染不区分 packed) |
|
||||
| 13 | `module_set_named_type_packed` | 语义不变(记录 `NamedTypeNode.IsPacked`);MLIR 侧**已参与渲染**:具名结构体在使用处内联为 `!llvm.struct<"name", packed (…)>`(见 §2.10) |
|
||||
| 14 | 中间指令文本(`Line.Buf`) | **内容与 llvmlite 不同**:builders 内嵌类型经 `_type_str` → `TypePrint` 已是 MLIR 形态(`load i32, !llvm.ptr %p` 而非 `load i32, i32* %p`)。文本层使用者(窥孔、verify)按名称/结构匹配,语义不受影响 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 待确认(不做静默假设,逐条标注)
|
||||
|
||||
1. `switch` 的方言形态按设计规范输出 `llvm.switch %v, ^default [0: ^case0, …]`,
|
||||
未用 `mlir-opt` 校验(真实 MLIR 可能要求 `%v : i32, ^default [ … ]` 形式)。
|
||||
1. ~~`switch` 的方言形态按设计规范输出 `llvm.switch %v, ^default [0: ^case0, …]`~~ ——
|
||||
**已由 mlir-opt 22.1.8 实测纠正**:规范里的形态被拒(`expected ':'`),
|
||||
实际采用 `llvm.switch %v : i32, ^default [0: ^case0]`(值类型紧随值),
|
||||
目标块实参写作 `^d(%c1 : i32)` / `[0: ^c0(%c0 : i32)]`(见 §2.2)。
|
||||
本轮同样实测了 `alloca {alignment = N : i64}`(§2.7)与
|
||||
`inline_asm … asm_dialect = intel …`(§2.8),三者均以实测形态为准。
|
||||
2. `alloca` / `load` / `store` 的 `volatile`、`atomicrmw` 的 ordering 关键字按
|
||||
LLVM 词法直译为 MLIR 词法,未用 `mlir-opt` 校验。
|
||||
3. 全局 `{section = "…", alignment = N : i64}` 属性字典的**位置**(值之后、类型之前)
|
||||
未用 `mlir-opt` 校验。
|
||||
4. `c"hello\00"` → `"hello"`:按设计规范**去掉尾随 `\00`**(设计规范样例为
|
||||
`@.str("hello") : !llvm.array<6 x i8>`)。严格 MLIR 语义下 `!llvm.array<6 x i8>`
|
||||
需要 6 字节;若工具链要求显式 NUL,去掉 `_MlirCStringToMlir` 里的剥离逻辑即可。
|
||||
LLVM 词法直译为 MLIR 词法。其中 `acq_rel` / `monotonic` 已随本轮的 `cmpxchg`
|
||||
用例一并由 `mlir-opt` 实测(§2.9);`volatile` 仍未单独校验。
|
||||
3. ~~全局 `{section = "…", alignment = N : i64}` 属性字典的**位置**(值之后、类型之前)
|
||||
未用 `mlir-opt` 校验~~ ——
|
||||
**已由 mlir-opt 22.1.8 实测确立**:字典**必须在类型之前**;放到类型之后会被当成
|
||||
新 op(``Dialect `' not found for custom op 'section'``),`unnamed_addr` 只能作
|
||||
关键字(进字典报 `occurs more than once`);stub 的 `external` 声明必须带空括号
|
||||
`()`。详见 §2.5 的硬约束 4/5。
|
||||
4. ~~`c"hello\00"` → `"hello"`:按设计规范**去掉尾随 `\00`**~~ ——
|
||||
**与实测冲突,已按实测推翻**:MLIR 要求字符串属性长度**等于** i8 数组长度,
|
||||
而 TPV 的类型含尾随 NUL(`[N+1 x i8]`)⇒ 属性里**必须保留 `\00`**
|
||||
(`_MlirCStringToMlir` 只剥 `c` 前缀,见 §2.5 硬约束 2)。
|
||||
5. `Func(Void, …)` 的类型文本渲染为 `!llvm.func<none (…)>`(由 `Void → none` 规则
|
||||
组合而来);函数签名位置的 void 返回值则是**省略** `-> ty`(不影响)。
|
||||
6. 渲染期的常量表 / 别名表 / 解析 scratch 都是**模块级全局**(每次 `FunctionPrint`
|
||||
@@ -303,6 +583,26 @@ module {
|
||||
放大编译器内存池;后续若要降内存,可改为"同一池内一次性分配并复用"。
|
||||
8. `MlirFail` 采用 `stdio.printf` + `stdlib.exit(1)` 的 fail-fast 风格(对齐 llvmlite
|
||||
的 `[IR-DUMP]`/`[IR-FAIL]`):不支持形态会**终止进程**而非返回错误码。
|
||||
拿不到调用栈,故"类型文本转换失败"这条报文会**追加当前正在渲染的中间文本行**
|
||||
(`… :'<类型片断>' [行: %1 = call …]`);行上下文由 `__function.vp` 的
|
||||
`_MlirRenderLine` 入口经 `__types.vp` 的 `MlirSetContextLine` 寄存
|
||||
(`FunctionPrint` / `PrintModule` 入口清空,避免带上无关旧行)。
|
||||
具名结构体的报错同样带类型名与行上下文(`_MlirFailNamed`,见 §2.10)。
|
||||
9. 具名结构体登记表(§2.10)是**打印期**设施:只在 `PrintModule` 期间有效
|
||||
(先数 `NamedTypeHead` 实际长度 → `MlirNamedTypesBegin(pool, cnt)` 精确分配
|
||||
`cnt * 16` 字节,每槽 `{ LLVMType*, IsPacked }` → 逐项
|
||||
`MlirNamedTypeRegister(ty, is_packed)` → 打印结束 `MlirNamedTypesClear`)。
|
||||
**无具名类型的模块零分配**;真实模块的具名类型可达数千个(实测单文件最多 7837 个),
|
||||
故不设固定容量上限。**独立调用 `FunctionPrint`** 时表未发布 ⇒ 中间文本里的
|
||||
`%"name"` 会响亮报错(builders 不会产生该形态,故正常路径不受影响)。
|
||||
10. `_mlir_read_type` 按 `[] {} () <>` 做**字符级**括号平衡来读 `!llvm.struct<…>`;
|
||||
具名结构的名字是**带引号字符串**,含 `[`/`]`(泛型特化名如
|
||||
`100000000000000a.GSList[100000000000000b.BasicBlock]`)时不影响平衡(成对出现)。
|
||||
实测语料(全仓 1462 个 `.ll`/`.vpi`,26476 个唯一具名类型):TPV 名字**全部成对**、
|
||||
无空格、无 `"`/`\`;仅 19 个非 TPV 名(手工 IR 里的 `<<.1` / `>>.10` 类移位临时名)
|
||||
含不配对 `<>` —— 它们不经 mlirlite(本库是**生成器**,只打印 TPV 模型)。
|
||||
MLIR 侧对这些名字的承载已实测:`!llvm.struct<">>.1", (i32)>` exit 0,
|
||||
降级得 `%">>.1" = type { i32 }`。
|
||||
|
||||
---
|
||||
|
||||
@@ -321,4 +621,8 @@ includes/mlirlite/
|
||||
```
|
||||
|
||||
测试:`Test/MLIRLiteTest/`(`App/main.vp` + `project.json`),用 `testcheck`
|
||||
断言各渲染规则(类型 / 函数 / 模块 / 常量物化 / phi→块参数 / 内存·调用·转换 / 全局 / verify)。
|
||||
断言各渲染规则(类型 / 函数 / 模块 / 常量物化 / phi→块参数 / 内存·调用·转换 / 全局 / verify /
|
||||
alloca 对齐·switch·inline_asm / 变参·va_arg·cmpxchg·全局属性字典(T14)/
|
||||
**具名结构体:按值·指针·文本 `%"name"` 展开·定义行丢弃(T15,见 §2.10)**/
|
||||
**多结果内联汇编·`extractvalue`(含多级索引,T16,见 §2.8)**)。
|
||||
其中 `output/*.mlir` 是 mlir-opt 的离线校验集(裸函数/类型/值文本落 `.txt`)。
|
||||
+556
-28
@@ -8,7 +8,7 @@ import stdlib
|
||||
from linkedlist import GSListNode, GSList
|
||||
from .__types import (
|
||||
LLVMType, ParamNode, TypePrint,
|
||||
MlirTypeText, MlirTypeTextIsPointer, MlirFail,
|
||||
MlirTypeText, MlirTypeTextIsPointer, MlirFail, MlirSetContextLine,
|
||||
)
|
||||
|
||||
|
||||
@@ -1160,11 +1160,17 @@ _MLIR_ConstVal: t.CPtr = None
|
||||
_MLIR_ConstName: t.CPtr = None
|
||||
_MLIR_ConstCount: int = 0
|
||||
|
||||
# bitcast no-op 别名表:结果名 -> 源操作数(ptr→ptr bitcast 在 MLIR 无意义)
|
||||
# bitcast no-op 别名表 + cmpxchg 结果绑定表:结果名 -> 源操作数 / extractvalue 结果名
|
||||
# (cmpxchg 的 extractvalue 结果名由结果名确定性派生,不再用计数器,见 _MlirCxResName)
|
||||
_MLIR_AliasFrom: t.CPtr = None
|
||||
_MLIR_AliasTo: t.CPtr = None
|
||||
_MLIR_AliasCount: int = 0
|
||||
|
||||
# 当前正在渲染的中间指令文本行(_MlirRenderLine 入口赋值)。
|
||||
# 供类型文本转换失败的报文追加行上下文:MlirFail 是即时终止、拿不到调用栈,
|
||||
# 故把行文本寄存到 __types.vp(经 MlirSetContextLine),由报错处一并打印。
|
||||
_MLIR_CurLine: str = None
|
||||
|
||||
# 解析期 scratch 缓冲(每次 FunctionPrint 分配一次,全程复用;
|
||||
# 所有需要跨行存活的文本在入表/入别名表时都会 dup,见 _mlir_dup)
|
||||
_SC_Op: str = None
|
||||
@@ -1334,6 +1340,26 @@ def _MlirConstGet(tytext: str, valtext: str) -> str:
|
||||
return nm
|
||||
|
||||
|
||||
def _MlirCxResName(resname: str, suffix: str) -> str:
|
||||
"""cmpxchg 的 extractvalue 结果名:由 struct 结果名**确定性派生**
|
||||
|
||||
MLIR 裸 SSA 名首字符必须是字母或 `_`(mlir-opt 22.1.8 实测:`%0_v` 报
|
||||
"expected '=' after SSA name"),故统一在结果名正文前加 `_`:
|
||||
`%0` → `%_0_v`(旧值 [0])/ `%_0_ok`(成功位 [1])
|
||||
`%a` → `%_a_v` / `%_a_ok`
|
||||
名字与结果名一一对应、不含计数器 ⇒ pass1(登记别名)与 pass2(发射)
|
||||
生成的名字必然逐字相同,引用不会悬空。
|
||||
"""
|
||||
nm: str = _MLIR_Pool.alloc(64)
|
||||
if nm is None:
|
||||
MlirFail("MLIR 渲染: cmpxchg 结果名分配失败")
|
||||
nm[0] = '\0'
|
||||
_append_cstr(nm, 64, "%_")
|
||||
_append_cstr(nm, 64, resname + 1)
|
||||
_append_cstr(nm, 64, suffix)
|
||||
return nm
|
||||
|
||||
|
||||
def _MlirParseHexU64(s: str) -> t.CUInt64T:
|
||||
"""解析 0x 十六进制字面量为 u64"""
|
||||
p: str = s
|
||||
@@ -1481,11 +1507,15 @@ def _mlir_read_value(p: str, out: str, outsz: sizet) -> str:
|
||||
return p
|
||||
|
||||
|
||||
def _mlir_read_type(p: str, out: str, outsz: sizet) -> str:
|
||||
def _mlir_read_type(p: str, out: str, outsz: sizet, nofunc: int = 0) -> str:
|
||||
"""读一个类型记号(MLIR 形态 !llvm.… 或标量 iN/fN,兼容 LLVM 聚合形态)
|
||||
|
||||
括号平衡覆盖 `[] {} () <>`:MLIR 的 !llvm.array<…>/!llvm.struct<(…)> 里含
|
||||
空格与逗号,必须靠深度计数整体读出;无括号类型(!llvm.ptr)遇到顶层分隔符结束。
|
||||
|
||||
nofunc=1 时**不吞并**紧随其后的 ` (…)` 函数类型后缀:call 的返回类型位置
|
||||
上,`(…)`(LLVM 词法)是**被调函数的参数类型表**而不是返回类型的一部分,
|
||||
必须留给 _MlirCall 的变参分支处理(见该处注释)。
|
||||
"""
|
||||
p = _mlir_ws(p)
|
||||
n: sizet = 0
|
||||
@@ -1539,9 +1569,9 @@ def _mlir_read_type(p: str, out: str, outsz: sizet) -> str:
|
||||
out[n] = p[0]
|
||||
n += 1
|
||||
p = p + 1
|
||||
# 函数类型后缀 " ( … )"
|
||||
# 函数类型后缀 " ( … )"(nofunc=1 时不吞并,见函数 docstring)
|
||||
q: str = _mlir_ws(p)
|
||||
if q[0] == '(':
|
||||
if nofunc == 0 and q[0] == '(':
|
||||
if n + 1 < outsz:
|
||||
out[n] = ' '
|
||||
n += 1
|
||||
@@ -1973,13 +2003,51 @@ def _MlirCmp(p: str, resname: str, ob: str, out: str, outsz: sizet):
|
||||
|
||||
|
||||
def _MlirAlloca(p: str, resname: str, out: str, outsz: sizet):
|
||||
"""alloca: llvm.alloca %cN x ty : (i32) -> !llvm.ptr"""
|
||||
"""alloca: llvm.alloca %cN x ty {alignment = N : i64} : (i32) -> !llvm.ptr
|
||||
|
||||
对齐属性的 MLIR 形态由 mlir-opt 22.1.8 实测确定(**不得凭记忆**):
|
||||
`{alignment = 16 : i64}` → exit 0 ✓(采用)
|
||||
`align(16)` / `, align 16`(LLVM 词法) → exit 1(expected ':')
|
||||
`{alignment = 16 : i32}` → exit 1
|
||||
("attribute 'alignment' failed to satisfy constraint: 64-bit signless
|
||||
integer attribute"——必须是 **i64**)
|
||||
故中间文本 `, align N` 必须转写为 `{alignment = N : i64}`。
|
||||
|
||||
对齐值只接受**常量十进制**且为 **2 的幂**:mlir-opt 不校验幂次
|
||||
(实测 `{alignment = 3 : i64}` exit 0),但非 2 的幂在 LLVM 语义里非法,
|
||||
故本层把关,非常量/非幂/超范围(> 2^30)一律响亮报错。
|
||||
"""
|
||||
tyb: str = _SC_Ty
|
||||
p = _mlir_read_type(p, tyb, 512)
|
||||
p = _mlir_ws(p)
|
||||
aligntxt: str = _SC_P2
|
||||
aligntxt[0] = '\0'
|
||||
if p[0] == ',':
|
||||
# ", align N" —— MLIR 对齐属性形态未经 mlir-opt 验证,v1 响亮报错
|
||||
MlirFail("MLIR 渲染: alloca 对齐(build_alloca_aligned)v1 不支持")
|
||||
# ", align N"(build_alloca_aligned 的中间文本形态)
|
||||
p = _mlir_ws(p + 1)
|
||||
if string.strncmp(p, "align", 5) != 0:
|
||||
MlirFail("MLIR 渲染: alloca 只支持 align 属性(未知属性)")
|
||||
p = _mlir_ws(p + 5)
|
||||
nd: int = 0
|
||||
while p[0] >= '0' and p[0] <= '9':
|
||||
if nd + 1 >= 512:
|
||||
MlirFail("MLIR 渲染: alloca 对齐值过长(超范围)")
|
||||
aligntxt[nd] = p[0]
|
||||
nd += 1
|
||||
p = p + 1
|
||||
aligntxt[nd] = '\0'
|
||||
p = _mlir_ws(p)
|
||||
if p[0] != '\0':
|
||||
MlirFail("MLIR 渲染: alloca 的 align 之后出现多余记号")
|
||||
if nd == 0:
|
||||
MlirFail("MLIR 渲染: alloca 对齐值必须是常量(常量十进制,非 SSA)")
|
||||
if nd > 10:
|
||||
MlirFail("MLIR 渲染: alloca 对齐值超范围(须为 2 的幂且 <= 2^30)")
|
||||
av: int = string.atoi(aligntxt)
|
||||
if av <= 0 or av > 1073741824:
|
||||
MlirFail("MLIR 渲染: alloca 对齐值超范围(须为 2 的幂且 <= 2^30)")
|
||||
if (av & (av - 1)) != 0:
|
||||
MlirFail("MLIR 渲染: alloca 对齐值必须是 2 的幂")
|
||||
mt: str = _SC_Mt
|
||||
mt[0] = '\0'
|
||||
MlirTypeText(mt, 512, tyb, _MLIR_Pool)
|
||||
@@ -1989,6 +2057,10 @@ def _MlirAlloca(p: str, resname: str, out: str, outsz: sizet):
|
||||
_append_cstr(out, outsz, cnt)
|
||||
_append_cstr(out, outsz, " x ")
|
||||
_append_cstr(out, outsz, mt)
|
||||
if aligntxt[0] != '\0':
|
||||
_append_cstr(out, outsz, " {alignment = ")
|
||||
_append_cstr(out, outsz, aligntxt)
|
||||
_append_cstr(out, outsz, " : i64}")
|
||||
_append_cstr(out, outsz, " : (i32) -> !llvm.ptr\n")
|
||||
|
||||
|
||||
@@ -2157,16 +2229,68 @@ def _MlirUnreachable(out: str, outsz: sizet):
|
||||
|
||||
|
||||
def _MlirCall(p: str, resname: str, out: str, outsz: sizet):
|
||||
"""call: llvm.call @f(%a) : (i32) -> i32 / 间接 llvm.call %fp(%a) : !llvm.ptr, (i32) -> i32"""
|
||||
"""call: llvm.call @f(%a) : (i32) -> i32 / 间接 llvm.call %fp(%a) : !llvm.ptr, (i32) -> i32
|
||||
|
||||
变参调用(中间文本 `call <ret> (…) @f(…)`)追加 `vararg(!llvm.func<…>)`。
|
||||
"""
|
||||
p = _mlir_ws(p)
|
||||
variadic: int = 0
|
||||
vftys: str = _SC_Tmp
|
||||
vftys[0] = '\0'
|
||||
rtyb: str = _SC_Ty
|
||||
p = _mlir_read_type(p, rtyb, 512)
|
||||
# 返回类型必须用 nofunc=1 读:变参调用的中间文本里,返回类型之后的 `(…)`
|
||||
# 是**被调函数的参数类型表**(LLVM 词法),不是返回类型的一部分。若按默认
|
||||
# 行为把它当"函数类型后缀"吞进 rtyb,则 p 落到 `@`/`%`、下面的变参分支
|
||||
# 永远进不去,rtyb 变成 `i32 (!llvm.ptr, i32, ...)` 送进 MlirTypeText →
|
||||
# _MlirFuncText 按逗号切参数,末片 `...` 无对应形态 → 误报
|
||||
# "无法转换为 MLIR 类型文本(v1 未覆盖该形态):'...'"。
|
||||
p = _mlir_read_type(p, rtyb, 512, 1)
|
||||
p = _mlir_ws(p)
|
||||
if string.strncmp(p, "asm", 3) == 0:
|
||||
# 内联汇编按 builder 形态渲染为 "call <ret_ty> asm ..."
|
||||
MlirFail("MLIR 渲染: inline_asm 的 MLIR 形态未固化,v1 不支持")
|
||||
# 内联汇编:中间文本是 `[%N = ]call <ret_ty> asm [sideeffect]
|
||||
# [inteldialect] "<asm>", "<constraints>"(<ty> %v, …)`(LLVM 词法),
|
||||
# MLIR 形态由 _MlirAsm 转写。
|
||||
_MlirAsm(p, resname, rtyb, out, outsz)
|
||||
return
|
||||
if p[0] == '(':
|
||||
MlirFail("MLIR 渲染: 变参 call 的 MLIR 形态未固化,v1 不支持")
|
||||
# 变参调用:中间文本是 `call <ret> (<实参类型…>, ...) @f(<实参>)`
|
||||
# (builder 的 build_call is_variadic=1 形态)。
|
||||
# MLIR 形态(mlir-opt 22.1.8 实测 exit 0):
|
||||
# %0 = llvm.call @printf(%p, %n) vararg(!llvm.func<i32 (!llvm.ptr, i32, ...)>)
|
||||
# : (!llvm.ptr, i32) -> i32
|
||||
# `vararg(...)` 里是**被调函数的完整 MLIR 函数类型**(含 `...`);
|
||||
# void 返回时该处返回类型写 `void`(写 `none` 实测报
|
||||
# "expected var_callee_type to return void")。
|
||||
# 注:vftys/calb 等缓冲用非 _SC_Ty(rtyb 占着)/非 _SC_Tmp2(实参循环用)。
|
||||
variadic = 1
|
||||
p = p + 1
|
||||
g0: int = 0
|
||||
while p[0] != '\0':
|
||||
g0 += 1
|
||||
if g0 > 256:
|
||||
MlirFail("MLIR 渲染: 变参 call 的函数类型参数过多")
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == ')':
|
||||
p = p + 1
|
||||
break
|
||||
if vftys[0] != '\0':
|
||||
_append_cstr(vftys, 4096, ", ")
|
||||
if p[0] == '.':
|
||||
# `...` 变参记号直通(不是类型,MlirTypeText 不认该记号)
|
||||
_append_cstr(vftys, 4096, "...")
|
||||
while p[0] == '.':
|
||||
p = p + 1
|
||||
else:
|
||||
ftb: str = _SC_V2
|
||||
ftb[0] = '\0'
|
||||
p = _mlir_read_type(p, ftb, 512)
|
||||
fmt: str = _SC_IdxT
|
||||
fmt[0] = '\0'
|
||||
MlirTypeText(fmt, 1024, ftb, _MLIR_Pool)
|
||||
_append_cstr(vftys, 4096, fmt)
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == ',':
|
||||
p = p + 1
|
||||
calb: str = _SC_Callee
|
||||
p = _mlir_read_value(p, calb, 512)
|
||||
p = _mlir_ws(p)
|
||||
@@ -2214,7 +2338,21 @@ def _MlirCall(p: str, resname: str, out: str, outsz: sizet):
|
||||
_append_cstr(out, outsz, calb)
|
||||
_append_cstr(out, outsz, "(")
|
||||
_append_cstr(out, outsz, args)
|
||||
_append_cstr(out, outsz, ") : ")
|
||||
_append_cstr(out, outsz, ")")
|
||||
if variadic != 0:
|
||||
# 变参调用:vararg(被调函数的完整 MLIR 函数类型)(含 `...`)
|
||||
_append_cstr(out, outsz, " vararg(!llvm.func<")
|
||||
if string.strcmp(rmt, "none") == 0:
|
||||
# void 返回的变参函数类型:返回类型写 `void`,不能写 `none`
|
||||
_append_cstr(out, outsz, "void")
|
||||
else:
|
||||
_append_cstr(out, outsz, rmt)
|
||||
_append_cstr(out, outsz, " (")
|
||||
_append_cstr(out, outsz, vftys)
|
||||
# 依次闭合三层:参数表 `)`、`!llvm.func<` 的 `>`、`vararg(` 的 `)`
|
||||
# (此前漏掉 vararg 的 `)`,mlir-opt 22.1.8 逐字报 "expected ')'")
|
||||
_append_cstr(out, outsz, ")>)")
|
||||
_append_cstr(out, outsz, " : ")
|
||||
if calb[0] != '@':
|
||||
# 间接调用:被调用者是 !llvm.ptr 操作数
|
||||
_append_cstr(out, outsz, "!llvm.ptr, ")
|
||||
@@ -2228,6 +2366,131 @@ def _MlirCall(p: str, resname: str, out: str, outsz: sizet):
|
||||
_append_cstr(out, outsz, "\n")
|
||||
|
||||
|
||||
def _MlirAsm(p: str, resname: str, rtyb: str, out: str, outsz: sizet):
|
||||
"""内联汇编: llvm.inline_asm [has_side_effects] [asm_dialect = intel] "…", "…" %a, %b : (i32, i32) -> i32
|
||||
|
||||
中间文本(LLVM 词法)有两条来源:
|
||||
builders 的 build_inline_asm → `call <ret> asm sideeffect "…", "…"()`
|
||||
TPV CallGenCBuiltin 注入文本 → `call <ret> asm sideeffect inteldialect "…", "…"(<ty> %v, …)`
|
||||
MLIR(llvm 方言) 形态由 mlir-opt 22.1.8 实测确定(**不得凭记忆**):
|
||||
* 方言关键字是 `asm_dialect = intel`:LLVM 的裸 `inteldialect` 报
|
||||
"expected attribute value";`asm_dialect = inteldialect` 报
|
||||
"expected string or keyword containing one of the following enum values
|
||||
for attribute 'asm_dialect' [att, intel]"。
|
||||
* 输入操作数是**逗号分隔的裸操作数、不带括号**:`"…", "=r,r"(%x) : (i32) -> i32`
|
||||
报 "expected ':'"(那正是 LLVM 词法,须改写)。
|
||||
* 无返回 ⇒ `: (<操作数类型…>) -> ()`;有返回 ⇒ `-> ty`。
|
||||
* **多结果**内联汇编在 MLIR 里是**单结果 + 结构体类型**:TPV 形态
|
||||
`%N = call {i32, i32} asm sideeffect "…", "=r,r"()` 渲染为
|
||||
`%N = llvm.inline_asm has_side_effects "…", "=r,r" : () -> !llvm.struct<(i32, i32)>`
|
||||
(mlir-opt / mlir-translate 22.1.8 实测 exit 0,降级得
|
||||
`call { i32, i32 } asm sideeffect …`),随后由 `llvm.extractvalue`
|
||||
逐字段抽取(见 _MlirExtractValue)。此处无需特殊分支:返回类型文本经
|
||||
MlirTypeText(`{…}` → `!llvm.struct<(...)>`)即可,嵌套结构体亦同。
|
||||
* 实测通过组合:有/无 has_side_effects × 有/无 asm_dialect = intel ×
|
||||
0/1/2 个操作数 × 有/无返回值。
|
||||
非法形态一律响亮报错:除 `sideeffect`/`inteldialect` 之外的未知记号、
|
||||
缺约束串、非字符串字面量、实参不成对、有返回值却无结果名。
|
||||
"""
|
||||
p = _mlir_ws(p + 3)
|
||||
side: int = 0
|
||||
intel: int = 0
|
||||
g: int = 0
|
||||
while g < 8:
|
||||
g += 1
|
||||
if string.strncmp(p, "sideeffect", 10) == 0 and (p[10] == ' ' or p[10] == '\t'):
|
||||
side = 1
|
||||
p = _mlir_ws(p + 10)
|
||||
continue
|
||||
if string.strncmp(p, "inteldialect", 12) == 0 and (p[12] == ' ' or p[12] == '\t'):
|
||||
intel = 1
|
||||
p = _mlir_ws(p + 12)
|
||||
continue
|
||||
break
|
||||
# 汇编文本可长达数 KB(TPV 的 ASM_LINE_BUF_SIZE=4096),用 4096 缓冲承载,
|
||||
# 不落小缓冲以免截断;转义对(\22/\5C/\0A)原样透传——MLIR 词法同样接受。
|
||||
asmb: str = _SC_Tmp
|
||||
p = _mlir_read_value(p, asmb, 4096)
|
||||
if asmb[0] != '"':
|
||||
MlirFail("MLIR 渲染: inline_asm 缺少汇编文本(或含未知关键字)")
|
||||
p = _mlir_ws(p)
|
||||
if p[0] != ',':
|
||||
MlirFail("MLIR 渲染: inline_asm 缺少约束串(形态须为 \"asm\", \"constraints\")")
|
||||
p = _mlir_ws(p + 1)
|
||||
conb: str = _SC_Args
|
||||
p = _mlir_read_value(p, conb, 2048)
|
||||
if conb[0] != '"':
|
||||
MlirFail("MLIR 渲染: inline_asm 约束串必须是字符串字面量")
|
||||
p = _mlir_ws(p)
|
||||
if p[0] != '(':
|
||||
MlirFail("MLIR 渲染: inline_asm 缺少实参列表 '(...)'")
|
||||
p = p + 1
|
||||
ops: str = _SC_ArgTs
|
||||
ops[0] = '\0'
|
||||
tys: str = _SC_IdxV
|
||||
tys[0] = '\0'
|
||||
nops: int = 0
|
||||
g = 0
|
||||
while p[0] != '\0':
|
||||
g += 1
|
||||
if g > 256:
|
||||
MlirFail("MLIR 渲染: inline_asm 实参过多")
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == ')':
|
||||
break
|
||||
atyb: str = _SC_Ty2
|
||||
p = _mlir_read_type(p, atyb, 512)
|
||||
p = _mlir_ws(p)
|
||||
avb: str = _SC_V1
|
||||
p = _mlir_read_value(p, avb, 512)
|
||||
if nops > 0:
|
||||
_append_cstr(ops, 2048, ", ")
|
||||
_append_cstr(tys, 1024, ", ")
|
||||
_MlirOperand(avb, atyb, ops, 2048)
|
||||
amt: str = _SC_Mt
|
||||
amt[0] = '\0'
|
||||
MlirTypeText(amt, 512, atyb, _MLIR_Pool)
|
||||
_append_cstr(tys, 1024, amt)
|
||||
nops += 1
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == ',':
|
||||
p = p + 1
|
||||
if p[0] == ')':
|
||||
p = p + 1
|
||||
p = _mlir_ws(p)
|
||||
if p[0] != '\0':
|
||||
MlirFail("MLIR 渲染: inline_asm 实参列表之后出现多余记号")
|
||||
rmt: str = _SC_Buf
|
||||
rmt[0] = '\0'
|
||||
MlirTypeText(rmt, 512, rtyb, _MLIR_Pool)
|
||||
if string.strcmp(rmt, "none") == 0:
|
||||
_MlirEmitRes(out, outsz, None)
|
||||
else:
|
||||
if resname is None:
|
||||
MlirFail("MLIR 渲染: inline_asm 有返回值但缺少结果名(%N = call …)")
|
||||
_MlirEmitRes(out, outsz, resname)
|
||||
_append_cstr(out, outsz, "llvm.inline_asm")
|
||||
if side != 0:
|
||||
_append_cstr(out, outsz, " has_side_effects")
|
||||
if intel != 0:
|
||||
_append_cstr(out, outsz, " asm_dialect = intel")
|
||||
_append_cstr(out, outsz, " ")
|
||||
_append_cstr(out, outsz, asmb)
|
||||
_append_cstr(out, outsz, ", ")
|
||||
_append_cstr(out, outsz, conb)
|
||||
if nops > 0:
|
||||
_append_cstr(out, outsz, " ")
|
||||
_append_cstr(out, outsz, ops)
|
||||
_append_cstr(out, outsz, " : (")
|
||||
_append_cstr(out, outsz, tys)
|
||||
_append_cstr(out, outsz, ") -> ")
|
||||
if string.strcmp(rmt, "none") == 0:
|
||||
_append_cstr(out, outsz, "()")
|
||||
else:
|
||||
_append_cstr(out, outsz, rmt)
|
||||
_append_cstr(out, outsz, "\n")
|
||||
|
||||
|
||||
def _MlirCast(p: str, resname: str, ob: str, out: str, outsz: sizet):
|
||||
"""转换指令: llvm.zext %v : i32 to i64"""
|
||||
ftyb: str = _SC_Ty
|
||||
@@ -2407,7 +2670,21 @@ def _MlirAppendCommaTypes(out: str, outsz: sizet, tylist: str):
|
||||
|
||||
|
||||
def _MlirSwitch(p: str, out: str, outsz: sizet):
|
||||
"""switch: llvm.switch %v, ^default [0: ^case0, 1: ^case1]"""
|
||||
"""switch: llvm.switch %v : i32, ^default [0: ^case0, 1: ^case1]
|
||||
|
||||
形态由 mlir-opt 22.1.8 实测确定(设计规范里的
|
||||
`llvm.switch %v, ^default [0: ^case0] : i32` **被拒绝**:报 "expected ':'"):
|
||||
`llvm.switch %v : i32, ^default [0: ^c0, 1: ^c1]` → exit 0 ✓(采用)
|
||||
`llvm.switch %v : i32, ^default(%r : i32) [ … ]` → exit 0 ✓(default 块实参)
|
||||
`llvm.switch %v : i32, ^default [0: ^c0(%r : i32)]` → exit 0 ✓(case 块实参)
|
||||
`llvm.switch %v : i64, ^default [4294967296: ^c0]` → exit 0 ✓(case 值随值类型)
|
||||
`llvm.switch %v : i32, ^default [-1: ^c0]` → exit 0 ✓
|
||||
`llvm.switch %v : i32, ^default []` → exit 0 ✓(空 case 表合法)
|
||||
即**值类型紧跟值之后**(`%v : i32,`),块实参写法与 br/cond_br 完全同构,
|
||||
故 `_MlirBlockOperands` 的 phi→块实参设施在此原样复用 ⇒ **支持目标块带 phi**
|
||||
(匹配不到入边会响亮报错,绝不静默丢实参)。
|
||||
case 值仍必须是常量:MLIR 里 case 值是属性(case_values),SSA 值不可用。
|
||||
"""
|
||||
tyb: str = _SC_Ty
|
||||
p = _mlir_read_type(p, tyb, 512)
|
||||
p = _mlir_ws(p)
|
||||
@@ -2423,6 +2700,14 @@ def _MlirSwitch(p: str, out: str, outsz: sizet):
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == '[':
|
||||
p = p + 1
|
||||
# 先物化值操作数与其 MLIR 类型文本:case 循环里的 _MlirBlockOperands 会复用
|
||||
# _SC_Ty/_SC_Buf/_SC_P1/_SC_P2 等 scratch,捕获到独立缓冲后才安全。
|
||||
vop: str = _SC_IdxT
|
||||
vop[0] = '\0'
|
||||
_MlirOperand(vb, tyb, vop, 1024)
|
||||
vmt: str = _SC_IdxV
|
||||
vmt[0] = '\0'
|
||||
MlirTypeText(vmt, 1024, tyb, _MLIR_Pool)
|
||||
cases: str = _SC_Tmp2
|
||||
cases[0] = '\0'
|
||||
ncase: int = 0
|
||||
@@ -2448,23 +2733,27 @@ def _MlirSwitch(p: str, out: str, outsz: sizet):
|
||||
p = _mlir_read_value(p, cb, 512)
|
||||
if cvb[0] == '%':
|
||||
MlirFail("MLIR 渲染: switch case 值必须是常量(SSA 值不支持)")
|
||||
cbr: str = _MlirBlkRef(cb)
|
||||
if ncase > 0:
|
||||
_append_cstr(cases, 1024, ", ")
|
||||
_append_cstr(cases, 1024, cvb)
|
||||
_append_cstr(cases, 1024, ": ^")
|
||||
_append_cstr(cases, 1024, _MlirBlkRef(cb))
|
||||
_append_cstr(cases, 1024, cbr)
|
||||
# 目标块带 phi ⇒ 追加块实参(与 br/cond_br 同一设施)
|
||||
_MlirBlockOperands(cases, 1024, _MlirFindBlock(_MLIR_Func, cbr), _MLIR_SrcBlk)
|
||||
ncase += 1
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == ',':
|
||||
p = p + 1
|
||||
dr: str = _MlirBlkRef(db)
|
||||
if _MlirBlockHasPhi(_MlirFindBlock(_MLIR_Func, dr)) != 0:
|
||||
MlirFail("MLIR 渲染: switch 目标块带 phi(携带块实参)v1 不支持")
|
||||
_MlirEmitRes(out, outsz, None)
|
||||
_append_cstr(out, outsz, "llvm.switch ")
|
||||
_MlirOperand(vb, tyb, out, outsz)
|
||||
_append_cstr(out, outsz, vop)
|
||||
_append_cstr(out, outsz, " : ")
|
||||
_append_cstr(out, outsz, vmt)
|
||||
_append_cstr(out, outsz, ", ^")
|
||||
_append_cstr(out, outsz, dr)
|
||||
_MlirBlockOperands(out, outsz, _MlirFindBlock(_MLIR_Func, dr), _MLIR_SrcBlk)
|
||||
_append_cstr(out, outsz, " [")
|
||||
_append_cstr(out, outsz, cases)
|
||||
_append_cstr(out, outsz, "]\n")
|
||||
@@ -2513,18 +2802,227 @@ def _MlirAtomicRmw(p: str, resname: str, out: str, outsz: sizet):
|
||||
_append_cstr(out, outsz, "\n")
|
||||
|
||||
|
||||
def _MlirCmpXchg(p: str, resname: str, resname2: str,
|
||||
out: str, outsz: sizet):
|
||||
"""cmpxchg: MLIR 单结果 struct{T, i1} + 两条 extractvalue 导出旧值/成功位
|
||||
|
||||
中间文本(LLVM 词法,两态:builder 单结果 / 双结果前缀)
|
||||
%N = cmpxchg <ptr_ty> <ptr>, <cmp_ty> <cmp>, <new_ty> <new> <succ> <fail>
|
||||
%a, %b = cmpxchg …
|
||||
MLIR 形态(mlir-opt 22.1.8 实测 exit 0;cmpxchg 在 MLIR 里是**单结果**,
|
||||
结果为 struct{T, i1},不是两个 SSA 值):
|
||||
%N = llvm.cmpxchg %p, %c, %n acq_rel monotonic : !llvm.ptr, i32
|
||||
%_N_v = llvm.extractvalue %N[0] : !llvm.struct<(i32, i1)> ; 旧值(比较值)
|
||||
%_N_ok = llvm.extractvalue %N[1] : !llvm.struct<(i32, i1)> ; i1 成功位
|
||||
两条 extractvalue 的结果名由 struct 结果名**确定性派生**
|
||||
(_MlirCxResName,如 `%0` → `%_0_v`/`%_0_ok`)。
|
||||
两个结果名绑定:`%N`(单结果)/ `%a`(双结果首名)→ 旧值 `[0]`;
|
||||
双结果次名 `%b` → 成功位 `[1]`。绑定走 bitcast no-op 同一别名设施
|
||||
(_MlirAliasAdd),后续引用由 _MlirResolve 自动改写为 extractvalue 结果名。
|
||||
这样 builder 端 `build_cmpxchg` 返回的「T 类型 Value」语义(旧值)得以保持。
|
||||
"""
|
||||
ptyb: str = _SC_Ty
|
||||
p = _mlir_read_type(p, ptyb, 512)
|
||||
p = _mlir_ws(p)
|
||||
pb: str = _SC_V1
|
||||
p = _mlir_read_value(p, pb, 512)
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == ',':
|
||||
p = _mlir_ws(p + 1)
|
||||
ctyb: str = _SC_Ty2
|
||||
p = _mlir_read_type(p, ctyb, 512)
|
||||
p = _mlir_ws(p)
|
||||
cb: str = _SC_V2
|
||||
p = _mlir_read_value(p, cb, 512)
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == ',':
|
||||
p = _mlir_ws(p + 1)
|
||||
ntyb: str = _SC_B1
|
||||
p = _mlir_read_type(p, ntyb, 512)
|
||||
p = _mlir_ws(p)
|
||||
nb: str = _SC_B2
|
||||
p = _mlir_read_value(p, nb, 512)
|
||||
p = _mlir_ws(p)
|
||||
succ: str = _SC_P1
|
||||
p = _mlir_read_value(p, succ, 512)
|
||||
p = _mlir_ws(p)
|
||||
fail: str = _SC_P2
|
||||
_mlir_read_value(p, fail, 512)
|
||||
if resname is None:
|
||||
MlirFail("MLIR 渲染: cmpxchg 缺少结果名")
|
||||
# 两条 extractvalue 的结果名由 struct 结果名确定性派生(两遍必然同名)
|
||||
onm: str = _MlirCxResName(resname, "_v")
|
||||
snm: str = _MlirCxResName(resname, "_ok")
|
||||
_MlirAliasAdd(resname, onm)
|
||||
if resname2 is not None:
|
||||
_MlirAliasAdd(resname2, snm)
|
||||
# MLIR 类型文本(用非 _SC_Mt 缓冲:_MlirOperand 对字面量会占用 _SC_Mt)
|
||||
pmt: str = _SC_Tmp2
|
||||
pmt[0] = '\0'
|
||||
MlirTypeText(pmt, 1024, ptyb, _MLIR_Pool)
|
||||
vmt: str = _SC_IdxV
|
||||
vmt[0] = '\0'
|
||||
MlirTypeText(vmt, 1024, ctyb, _MLIR_Pool)
|
||||
stmt: str = _SC_IdxT
|
||||
stmt[0] = '\0'
|
||||
_append_cstr(stmt, 1024, "!llvm.struct<(")
|
||||
_append_cstr(stmt, 1024, vmt)
|
||||
_append_cstr(stmt, 1024, ", i1)>")
|
||||
_MlirEmitRes(out, outsz, resname)
|
||||
_append_cstr(out, outsz, "llvm.cmpxchg ")
|
||||
_MlirOperand(pb, ptyb, out, outsz)
|
||||
_append_cstr(out, outsz, ", ")
|
||||
_MlirOperand(cb, ctyb, out, outsz)
|
||||
_append_cstr(out, outsz, ", ")
|
||||
_MlirOperand(nb, ntyb, out, outsz)
|
||||
_append_cstr(out, outsz, " ")
|
||||
_append_cstr(out, outsz, succ)
|
||||
_append_cstr(out, outsz, " ")
|
||||
_append_cstr(out, outsz, fail)
|
||||
_append_cstr(out, outsz, " : ")
|
||||
_append_cstr(out, outsz, pmt)
|
||||
_append_cstr(out, outsz, ", ")
|
||||
_append_cstr(out, outsz, vmt)
|
||||
_append_cstr(out, outsz, "\n")
|
||||
_MlirEmitRes(out, outsz, onm)
|
||||
_append_cstr(out, outsz, "llvm.extractvalue ")
|
||||
_append_cstr(out, outsz, resname)
|
||||
_append_cstr(out, outsz, "[0] : ")
|
||||
_append_cstr(out, outsz, stmt)
|
||||
_append_cstr(out, outsz, "\n")
|
||||
_MlirEmitRes(out, outsz, snm)
|
||||
_append_cstr(out, outsz, "llvm.extractvalue ")
|
||||
_append_cstr(out, outsz, resname)
|
||||
_append_cstr(out, outsz, "[1] : ")
|
||||
_append_cstr(out, outsz, stmt)
|
||||
_append_cstr(out, outsz, "\n")
|
||||
|
||||
|
||||
def _MlirVaArg(p: str, resname: str, out: str, outsz: sizet):
|
||||
"""va_arg: 用**通用 op 语法**(刻意选择,见下)
|
||||
|
||||
中间文本(LLVM 词法): %N = va_arg <va_list_ty> <va_list_ptr>, <arg_ty>
|
||||
MLIR 文本: %N = "llvm.va_arg"(%ap) : (!llvm.ptr) -> i32
|
||||
|
||||
为什么必须用 generic 形态(mlir-opt 22.1.8 实测):自定义汇编语法
|
||||
`llvm.va_arg %ap, i32` 要求显式给出 va_list 的类型(LLVM 里是 `ptr`),
|
||||
而不透明指针下该类型无法表达 —— mlir-opt 报 "expected builtin.function",
|
||||
故改用通用 op 语法(generic form),只用操作数类型列表描述。
|
||||
降级验证(mlir-translate --mlir-to-llvmir)实测得到
|
||||
%2 = va_arg ptr %0, i32
|
||||
与 LLVM 的 va_arg 完全同形。
|
||||
"""
|
||||
vtyb: str = _SC_Ty
|
||||
p = _mlir_read_type(p, vtyb, 512)
|
||||
p = _mlir_ws(p)
|
||||
vb: str = _SC_V1
|
||||
p = _mlir_read_value(p, vb, 512)
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == ',':
|
||||
p = _mlir_ws(p + 1)
|
||||
atyb: str = _SC_Ty2
|
||||
_mlir_read_type(p, atyb, 512)
|
||||
if resname is None:
|
||||
MlirFail("MLIR 渲染: va_arg 缺少结果名")
|
||||
# 操作数类型 / 结果类型(_MlirOperand 只用 _SC_Mt,故此处先取独立缓冲)
|
||||
omt: str = _SC_Tmp2
|
||||
omt[0] = '\0'
|
||||
MlirTypeText(omt, 1024, vtyb, _MLIR_Pool)
|
||||
rmt: str = _SC_IdxV
|
||||
rmt[0] = '\0'
|
||||
MlirTypeText(rmt, 1024, atyb, _MLIR_Pool)
|
||||
_MlirEmitRes(out, outsz, resname)
|
||||
_append_cstr(out, outsz, "\"llvm.va_arg\"(")
|
||||
_MlirOperand(vb, vtyb, out, outsz)
|
||||
_append_cstr(out, outsz, ") : (")
|
||||
_append_cstr(out, outsz, omt)
|
||||
_append_cstr(out, outsz, ") -> ")
|
||||
_append_cstr(out, outsz, rmt)
|
||||
_append_cstr(out, outsz, "\n")
|
||||
|
||||
|
||||
def _MlirExtractValue(p: str, resname: str, out: str, outsz: sizet):
|
||||
"""extractvalue: llvm.extractvalue %v[i0, i1] : <聚合类型>
|
||||
|
||||
中间文本是 LLVM 语法(**聚合类型文本在值之前,索引在逗号之后**):
|
||||
%r = extractvalue {i32, i32} %v, 0
|
||||
%r = extractvalue {i32, {i32, i32}} %v, 1, 0 ; 多级索引
|
||||
MLIR 形态(mlir-opt 22.1.8 实测 exit 0):
|
||||
%r = llvm.extractvalue %v[0] : !llvm.struct<(i32, i32)>
|
||||
%r = llvm.extractvalue %v[1, 0] : !llvm.struct<(i32, !llvm.struct<(i32, i32)>)>
|
||||
索引列表在 MLIR 里写在 `[]` 内、以 `, ` 分隔(与 LLVM 的 `<聚合>, i0, i1`
|
||||
词法不同,必须转写);聚合类型文本经 MlirTypeText 转成 MLIR 类型
|
||||
(匿名 `{…}` → !llvm.struct<(...)>、具名 `%"name"` → 内联具名体、
|
||||
数组 `[N x T]` → !llvm.array<N x T>,三者均已实测 exit 0)。
|
||||
真实来源:TPV 的**多结果内联汇编**(`%asm_result = call {i32, i32} asm …`)
|
||||
之后逐字段抽取(`%asm_out_0 = extractvalue {i32, i32} %asm_result, 0`,
|
||||
语料实测均为单索引);多级索引同样经 mlir-opt 实测合法,故一并支持。
|
||||
索引必须是常量字面量(LLVM 词法如此)——SSA 索引 ⇒ 响亮报错。
|
||||
"""
|
||||
atyb: str = _SC_Ty
|
||||
p = _mlir_read_type(p, atyb, 512)
|
||||
p = _mlir_ws(p)
|
||||
vb: str = _SC_V1
|
||||
p = _mlir_read_value(p, vb, 512)
|
||||
idxs: str = _SC_IdxV
|
||||
idxs[0] = '\0'
|
||||
nidx: int = 0
|
||||
g: int = 0
|
||||
# 索引列表:LLVM 词法里逗号分隔(`<聚合>, i0, i1`),逐个读出后转写;
|
||||
# 循环上限与文件内其它解析循环同款(有界,超出即响亮报错)。
|
||||
while g < 64:
|
||||
p = _mlir_ws(p)
|
||||
if p[0] != ',':
|
||||
break
|
||||
g += 1
|
||||
if g > 32:
|
||||
MlirFail("MLIR 渲染: extractvalue 索引过多")
|
||||
p = _mlir_ws(p + 1)
|
||||
ib: str = _SC_IdxT
|
||||
p = _mlir_read_value(p, ib, 512)
|
||||
if ib[0] == '\0':
|
||||
MlirFail("MLIR 渲染: extractvalue 缺少索引")
|
||||
if ib[0] == '%':
|
||||
MlirFail("MLIR 渲染: extractvalue 索引必须是常量(SSA 索引不支持)")
|
||||
if nidx > 0:
|
||||
_append_cstr(idxs, 1024, ", ")
|
||||
_append_cstr(idxs, 1024, ib)
|
||||
nidx += 1
|
||||
if nidx == 0:
|
||||
MlirFail("MLIR 渲染: extractvalue 缺少索引")
|
||||
if p[0] != '\0':
|
||||
MlirFail("MLIR 渲染: extractvalue 索引之后出现多余记号")
|
||||
# MLIR 聚合类型文本先落独立缓冲(_MlirOperand 只占 _SC_Mt,不冲突)
|
||||
amt: str = _SC_Tmp2
|
||||
amt[0] = '\0'
|
||||
MlirTypeText(amt, 1024, atyb, _MLIR_Pool)
|
||||
_MlirEmitRes(out, outsz, resname)
|
||||
_append_cstr(out, outsz, "llvm.extractvalue ")
|
||||
_MlirOperand(vb, atyb, out, outsz)
|
||||
_append_cstr(out, outsz, "[")
|
||||
_append_cstr(out, outsz, idxs)
|
||||
_append_cstr(out, outsz, "] : ")
|
||||
_append_cstr(out, outsz, amt)
|
||||
_append_cstr(out, outsz, "\n")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 指令行 → MLIR 分派
|
||||
# ============================================================
|
||||
def _MlirRenderLine(text: str, out: str, outsz: sizet):
|
||||
"""解析一条中间指令文本并渲染为 MLIR(out=None 时仅登记/校验)"""
|
||||
global _MLIR_Pass
|
||||
global _MLIR_Pass, _MLIR_CurLine
|
||||
if text is None:
|
||||
return
|
||||
# 入口寄存"当前行":类型文本转换失败时随报文打印(两次 pass 都是同一行,
|
||||
# pass1 失败也能定位到具体中间文本)。text 指向 Line.Buf,生命周期覆盖整次渲染。
|
||||
_MLIR_CurLine = text
|
||||
MlirSetContextLine(_MLIR_CurLine)
|
||||
p: str = _mlir_ws(text)
|
||||
if p[0] == '\0':
|
||||
return
|
||||
resname: str = None
|
||||
resname2: str = None
|
||||
if p[0] == '%':
|
||||
tb: str = _SC_Tb
|
||||
q: str = _mlir_read_value(p, tb, 512)
|
||||
@@ -2532,10 +3030,24 @@ def _MlirRenderLine(text: str, out: str, outsz: sizet):
|
||||
if q2[0] == '=':
|
||||
resname = _mlir_dup(tb)
|
||||
p = _mlir_ws(q2 + 1)
|
||||
elif q2[0] == ',':
|
||||
# 双结果前缀 `%a, %b = <op>`(仅 cmpxchg):MLIR 的 cmpxchg 是**单结果**
|
||||
# struct{T, i1},两个名字分别绑定到两条 extractvalue(见 _MlirCmpXchg)。
|
||||
resname = _mlir_dup(tb)
|
||||
q3: str = _mlir_ws(q2 + 1)
|
||||
tb2: str = _SC_V3
|
||||
q3 = _mlir_read_value(q3, tb2, 512)
|
||||
q4: str = _mlir_ws(q3)
|
||||
if q4[0] != '=':
|
||||
MlirFail("MLIR 渲染: 双结果前缀(%a, %b =)缺少 '='")
|
||||
resname2 = _mlir_dup(tb2)
|
||||
p = _mlir_ws(q4 + 1)
|
||||
ob: str = _SC_Op
|
||||
p = _mlir_read_value(p, ob, 64)
|
||||
if ob[0] == '\0':
|
||||
MlirFail("MLIR 渲染: 空指令")
|
||||
if resname2 is not None and string.strcmp(ob, "cmpxchg") != 0:
|
||||
MlirFail("MLIR 渲染: 双结果前缀(%a, %b =)只支持 cmpxchg")
|
||||
if _mlir_in_set(ob, "|add|sub|mul|sdiv|udiv|srem|urem|and|or|xor|shl|lshr|ashr|") != 0:
|
||||
_MlirBinop(p, resname, ob, out, outsz)
|
||||
return
|
||||
@@ -2597,11 +3109,19 @@ def _MlirRenderLine(text: str, out: str, outsz: sizet):
|
||||
_MlirAtomicRmw(p, resname, out, outsz)
|
||||
return
|
||||
if string.strcmp(ob, "cmpxchg") == 0:
|
||||
MlirFail("MLIR 渲染: cmpxchg 的 MLIR 形态未固化,v1 不支持")
|
||||
_MlirCmpXchg(p, resname, resname2, out, outsz)
|
||||
return
|
||||
if string.strcmp(ob, "va_arg") == 0:
|
||||
MlirFail("MLIR 渲染: va_arg v1 不支持")
|
||||
# 注:内联汇编在中间文本里 opcode 是 "call"(call <ret> asm ...),
|
||||
# 由 _MlirCall 内部识别并响亮报错。
|
||||
_MlirVaArg(p, resname, out, outsz)
|
||||
return
|
||||
if string.strcmp(ob, "extractvalue") == 0:
|
||||
# 真实来源:TPV 的多结果内联汇编结果抽取(见 _MlirExtractValue)。
|
||||
# 注:`insertvalue` 等其它未支持 opcode 在语料中 0 命中,
|
||||
# 落到下面的兜底响亮报错(保持"未知指令 opcode"即可)。
|
||||
_MlirExtractValue(p, resname, out, outsz)
|
||||
return
|
||||
# 注:内联汇编在中间文本里 opcode 是 "call"(call <ret> asm …),
|
||||
# 由 _MlirCall 内部识别并转交 _MlirAsm 渲染(llvm.inline_asm)。
|
||||
MlirFail("MLIR 渲染: 未知指令 opcode")
|
||||
|
||||
|
||||
@@ -2643,10 +3163,14 @@ def _MlirFuncSig(buf: str, size: sizet,
|
||||
_append_cstr(buf, size, ty_buf)
|
||||
cur = cur.Next
|
||||
first = 0
|
||||
_append_cstr(buf, size, ")")
|
||||
# 变参函数:MLIR 形态未固化 → 响亮报错(见 README v1 不支持清单)
|
||||
# 变参函数:参数表尾追加 `...`(mlir-opt 22.1.8 实测 exit 0:
|
||||
# `llvm.func @f(%p: !llvm.ptr, ...) -> i32` / 无固定参数时 `llvm.func @f(...) -> i32`)
|
||||
if func.IsVarArg == 1:
|
||||
MlirFail("MLIR 渲染: 变参函数(...)的 llvm.func 形态未固化,v1 不支持")
|
||||
if params is not None and params.Head is not None:
|
||||
_append_cstr(buf, size, ", ...")
|
||||
else:
|
||||
_append_cstr(buf, size, "...")
|
||||
_append_cstr(buf, size, ")")
|
||||
# 返回类型(Void 省略)
|
||||
is_void: int = 0
|
||||
match func.RetTy:
|
||||
@@ -2669,7 +3193,7 @@ def FunctionPrint(buf: str, size: sizet,
|
||||
注意:与 llvmlite 版一致,本函数为追加写(供模块打印连续拼接多个函数),
|
||||
调用方若要单函数文本需自行先置 buf[0]='\\0'。
|
||||
"""
|
||||
global _MLIR_Pass, _MLIR_Func, _MLIR_SrcBlk
|
||||
global _MLIR_Pass, _MLIR_Func, _MLIR_SrcBlk, _MLIR_CurLine
|
||||
if func is None or buf is None or size == 0:
|
||||
return
|
||||
|
||||
@@ -2683,6 +3207,10 @@ def FunctionPrint(buf: str, size: sizet,
|
||||
|
||||
_MlirTablesInit(pool)
|
||||
_MLIR_Func = func
|
||||
# 清空上一函数的行上下文:签名/declare 等非"行驱动"位置若报类型错,
|
||||
# 不该带上一个已经无关的旧行,避免诊断误导(行上下文只在 _MlirRenderLine 赋值)。
|
||||
_MLIR_CurLine = None
|
||||
MlirSetContextLine(None)
|
||||
|
||||
if func.IsDeclared == 1:
|
||||
# declare(无函数体)
|
||||
|
||||
+647
-30
@@ -8,11 +8,13 @@ import memhub
|
||||
from linkedlist import GSList, GSListNode
|
||||
from .__types import (
|
||||
LLVMType, TypePrint, Array, PrintStructDefinition, get_struct_name, Struct,
|
||||
ParamNode, MlirFail, MlirTypeText,
|
||||
ParamNode, MlirFail, MlirTypeText, MlirSetContextLine,
|
||||
MlirNamedTypesBegin, MlirNamedTypesClear, MlirNamedTypeRegister,
|
||||
)
|
||||
from .__function import (
|
||||
Function, FunctionPrint, _ll_name_needs_quote, Param, param_get_next,
|
||||
param_get_ty, _mlir_read_type, _mlir_read_value, _mlir_ws,
|
||||
_MlirSymRefAppend,
|
||||
)
|
||||
|
||||
|
||||
@@ -43,7 +45,7 @@ class GlobalVariable(GSListNode[GlobalVariable]):
|
||||
@t.NoVTable
|
||||
class NamedTypeNode(GSListNode[NamedTypeNode]):
|
||||
Ty: rawptr[LLVMType] # 命名结构体类型指针
|
||||
IsPacked: int # 1=packed 结构体(输出 <{...}>,无字段填充)
|
||||
IsPacked: int # 1=packed 结构体(MLIR: !llvm.struct<"name", packed (…)>,无字段填充)
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -218,6 +220,16 @@ class LLVMModule:
|
||||
"""
|
||||
if self is None or buf is None or size == 0: return
|
||||
|
||||
# 清空行上下文:全局段不是"行驱动"渲染,报类型错时不该带上一函数残留的旧行
|
||||
# (行上下文由 __function.vp 的 _MlirRenderLine 赋值,见 __types.vp 的 _MlirCtxLine)。
|
||||
MlirSetContextLine(None)
|
||||
|
||||
# 发布本模块的命名结构体登记表:中间文本里"只有名字"的类型(`%"name"`)
|
||||
# 经它回查完整定义、展开为使用处的内联具名形态(!llvm.struct<"name", (…)>)。
|
||||
# 表随本次打印重建,打印结束(本函数末尾)即清空 —— 表内类型指针只在本次
|
||||
# 打印期间有效,清空后不再解引用(避免 pool reset 后悬空)。
|
||||
_MlirPublishNamedTypes(self, pool)
|
||||
|
||||
_append_cstr(buf, size, "module {\n")
|
||||
|
||||
# 模式说明注释(MLIR 无 stub/text 二分)
|
||||
@@ -280,7 +292,56 @@ class LLVMModule:
|
||||
_MlirAppendIndented(buf, size, tmp, 2, pool)
|
||||
func = func.Next
|
||||
|
||||
_append_cstr(buf, size, "}\n")
|
||||
_append_cstr(buf, size, "}\n")
|
||||
# 打印结束:清空命名结构体登记表(表内类型指针此后不再解引用)
|
||||
MlirNamedTypesClear()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _MlirPublishNamedTypes - 把模块的命名结构体链表发布给 __types.vp 的登记表
|
||||
#
|
||||
# 用途:类型文本里"只有名字"的形态(中间文本 `%"sha1.Cls"`)在 MLIR 侧必须
|
||||
# 展开成使用处的内联具名体(MLIR 无定义行、且不允许无体前向声明),故需要一个
|
||||
# 名字 → 完整定义的查表设施。NamedTypeHead 由 module_add_named_type 维护
|
||||
# (同名至多一项,opaque 会被后到的完整定义就地替换),这里在模块打印开始时
|
||||
# 逐项发布;opaque 项也发布,使引用它时报"仍为 opaque"(而不是"未登记")。
|
||||
# 同时把节点的 packed 标记(NamedTypeNode.IsPacked,设置点唯一:
|
||||
# module_set_named_type_packed)与类型一并登记,供渲染侧按名字回查。
|
||||
# ============================================================
|
||||
def _MlirPublishNamedTypes(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*):
|
||||
"""按 NamedTypeHead 重建命名结构体登记表(打印期查表用,含 packed 标记)
|
||||
|
||||
先数一遍真实链表长度(不依赖 NamedTypeCount 字段),再按该数量精确分配槽位、
|
||||
逐项登记 —— TPV 真实模块的具名类型可达数千个(见 README §5.9),故不做固定
|
||||
容量假设;无具名类型时不分配。
|
||||
"""
|
||||
if mod is None or pool is None:
|
||||
return
|
||||
cnt: sizet = 0
|
||||
nt: rawptr[NamedTypeNode] = mod.NamedTypeHead
|
||||
nt_steps: sizet = 0
|
||||
while nt is not None:
|
||||
nt_steps += 1
|
||||
if nt_steps > IR_DUMP_CHAIN_LIMIT:
|
||||
stdio.printf("[IR-DUMP] 链表成环: module named types count(NamedTypeCount=%zu,计数终止)\n",
|
||||
mod.NamedTypeCount)
|
||||
stdlib.exit(1)
|
||||
cnt += 1
|
||||
nt = nt.Next
|
||||
MlirNamedTypesBegin(pool, cnt)
|
||||
nt = mod.NamedTypeHead
|
||||
nt_steps = 0
|
||||
while nt is not None:
|
||||
nt_steps += 1
|
||||
if nt_steps > IR_DUMP_CHAIN_LIMIT:
|
||||
stdio.printf("[IR-DUMP] 链表成环: module named types publish(NamedTypeCount=%zu,发布终止)\n",
|
||||
mod.NamedTypeCount)
|
||||
stdlib.exit(1)
|
||||
if nt.Ty is not None:
|
||||
# packed 标记只挂在 NamedTypeNode 上(LLVMType.Struct 无该字段),
|
||||
# 设置点唯一:module_set_named_type_packed。发布时与类型一并登记。
|
||||
MlirNamedTypeRegister(nt.Ty, nt.IsPacked)
|
||||
nt = nt.Next
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -401,11 +462,14 @@ def module_add_named_type(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
|
||||
|
||||
def module_set_named_type_packed(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
|
||||
ty: rawptr[LLVMType], is_packed: int):
|
||||
"""按名称设置命名结构体的 packed 标记(输出 %"name" = type <{...}>)。
|
||||
"""按名称设置命名结构体的 packed 标记(MLIR 输出 !llvm.struct<"name", packed (…)>)。
|
||||
|
||||
编译器解析 @c.Attribute(t.attr.packed) 后调用,使 llc 按 packed 布局
|
||||
(无字段填充)计算字段偏移/GEP。ty 为命名 Struct 类型(按 get_struct_name
|
||||
定位已注册节点);未找到则静默跳过(opaque/跨模块 stub 场景无布局需求)。
|
||||
(无字段填充)计算字段偏移/GEP。MLIR 侧由打印期登记表携带该标记
|
||||
(_MlirPublishNamedTypes 读 NamedTypeNode.IsPacked),使用处内联形态加
|
||||
`packed ` 关键字。ty 为命名 Struct 类型(按 get_struct_name 定位已注册节点);
|
||||
未找到则静默跳过(opaque/跨模块 stub 场景无布局需求)。匿名结构体
|
||||
(Name 为 None)在此直接返回 —— packed 标记无法也没有位置挂在匿名结构体上。
|
||||
"""
|
||||
if mod is None or ty is None:
|
||||
return
|
||||
@@ -568,8 +632,13 @@ def LLVMModulePrint(buf: str, size: sizet,
|
||||
# llvm.mlir.global @counter(0 : i32) : i32
|
||||
# llvm.mlir.global external @x : i32
|
||||
# linkage/constant/unnamed_addr/section 映射为对应关键字/属性。
|
||||
# v1 支持的初值文本形态: 整数字面量、浮点、null、zeroinitializer、cstring、
|
||||
# 整型数组;其它形态(尤其 { … } 结构体/vtable 初始化串)**响亮报错**。
|
||||
# 初值有两条路径(判定见 _MlirGlobalInitNeedsRegion):
|
||||
# * 属性路径:整数字面量、浮点、cstring、字符串字面量、纯标量数组;
|
||||
# * region 路径:根级裸 null/zeroinitializer(属性形态 `(zeroinitializer)`
|
||||
# 被 mlir-opt 拒绝)、结构体/vtable、含符号引用的聚合、指针型初值 →
|
||||
# 空值括号 + `: 类型 {` 内的 initializer region
|
||||
# (叶子 op + undef + insertvalue 链 + return)。
|
||||
# 两条路径都覆盖不到的形态一律 MlirFail **响亮报错**,绝不静默输出错误 MLIR。
|
||||
# ============================================================
|
||||
def _MlirGlobalLine(buf: str, size: sizet,
|
||||
pool: memhub.MemBuddy*, gv: rawptr[GlobalVariable],
|
||||
@@ -606,25 +675,44 @@ def _MlirGlobalLine(buf: str, size: sizet,
|
||||
_append_cstr(buf, size, "\"")
|
||||
else:
|
||||
_append_cstr(buf, size, gv.Name)
|
||||
# 初值
|
||||
if stub == 0 and gv.Initializer is not None:
|
||||
_append_cstr(buf, size, "(")
|
||||
_MlirGlobalInit(buf, size, pool, gv)
|
||||
_append_cstr(buf, size, ")")
|
||||
# 属性(section / alignment 按属性字典输出;形态未经 mlir-opt 验证,见 README)
|
||||
if stub == 0 and (gv.Section is not None or gv.Align > 0):
|
||||
_append_cstr(buf, size, " {")
|
||||
if gv.Section is not None:
|
||||
_append_cstr(buf, size, "section = \"")
|
||||
_append_cstr(buf, size, gv.Section)
|
||||
_append_cstr(buf, size, "\"")
|
||||
if gv.Align > 0:
|
||||
_append_cstr(buf, size, ", ")
|
||||
if gv.Align > 0:
|
||||
ab: str = pool.alloc(48)
|
||||
if ab is not None:
|
||||
viperlib.snprintf(ab, 48, "alignment = %d : i64", gv.Align)
|
||||
_append_cstr(buf, size, ab)
|
||||
# 初值:聚合/含符号引用/根级裸零值形态改用 initializer region
|
||||
# (见 _MlirGlobalInitNeedsRegion)
|
||||
region: int = 0
|
||||
if stub == 0 and gv.Initializer is not None:
|
||||
region = _MlirGlobalInitNeedsRegion(gv.Initializer)
|
||||
if region == 0:
|
||||
_append_cstr(buf, size, "(")
|
||||
_MlirGlobalInit(buf, size, pool, gv)
|
||||
_append_cstr(buf, size, ")")
|
||||
else:
|
||||
# region 形态:值括号为空(初值由 region 内的 insertvalue 链构造)
|
||||
_append_cstr(buf, size, "()")
|
||||
else:
|
||||
# 无初值(stub=1 的 external 声明 / 无初值全局):**值括号必须保留**
|
||||
# ⚠ mlir-opt 22.1.8 实测:`llvm.mlir.global external @x : i32` 报
|
||||
# "expected '('";`llvm.mlir.global external @x() : i32` exit 0。
|
||||
_append_cstr(buf, size, "()")
|
||||
# 属性字典(section / alignment):**必须在类型之前**(值之后)输出。
|
||||
# ⚠ mlir-opt 22.1.8 实测确立(违反即解析失败):
|
||||
# * 正确 = `@"s"("ab\00") {section = "data", alignment = 16 : i64} : !llvm.array<3 x i8>`
|
||||
# (exit 0);
|
||||
# * 错误 = 字典放在**类型之后**(`… : !llvm.array<3 x i8> {section = "data"}`
|
||||
# 被当成新 op,报 "Dialect `' not found for custom op 'section'");
|
||||
# * `unnamed_addr` 是**关键字**(见上,在名字之前输出),**不能**放进字典
|
||||
# (放进字典报 "attribute 'unnamed_addr' occurs more than once")。
|
||||
if stub == 0 and (gv.Section is not None or gv.Align > 0):
|
||||
_append_cstr(buf, size, " {")
|
||||
if gv.Section is not None:
|
||||
_append_cstr(buf, size, "section = \"")
|
||||
_append_cstr(buf, size, gv.Section)
|
||||
_append_cstr(buf, size, "\"")
|
||||
if gv.Align > 0:
|
||||
_append_cstr(buf, size, ", ")
|
||||
if gv.Align > 0:
|
||||
ab: str = pool.alloc(48)
|
||||
if ab is not None:
|
||||
viperlib.snprintf(ab, 48, "alignment = %d : i64", gv.Align)
|
||||
_append_cstr(buf, size, ab)
|
||||
_append_cstr(buf, size, "}")
|
||||
# 类型
|
||||
_append_cstr(buf, size, " : ")
|
||||
@@ -633,6 +721,11 @@ def _MlirGlobalLine(buf: str, size: sizet,
|
||||
ty_buf[0] = '\0'
|
||||
TypePrint(ty_buf, 512, gv.Ty, pool)
|
||||
_append_cstr(buf, size, ty_buf)
|
||||
# initializer region(顶层聚合/指针初值:叶子 op + undef + insertvalue 链 + return)
|
||||
if region != 0:
|
||||
_append_cstr(buf, size, " {\n")
|
||||
_MlirGlobalInitRegion(buf, size, pool, gv)
|
||||
_append_cstr(buf, size, "}")
|
||||
_append_cstr(buf, size, "\n")
|
||||
|
||||
|
||||
@@ -660,10 +753,12 @@ def _MlirGlobalInit(buf: str, size: sizet,
|
||||
_MlirArrayDenseAt(buf, size, x, pool)
|
||||
return
|
||||
if string.strcmp(x, "null") == 0 or string.strcmp(x, "zeroinitializer") == 0:
|
||||
# 根级裸零值现由 region 路径处理(_MlirGlobalInitNeedsRegion 判 1),
|
||||
# 属性形态 `(zeroinitializer)` 已被 mlir-opt 拒绝,此处仅为兜底保留。
|
||||
_append_cstr(buf, size, "zeroinitializer")
|
||||
return
|
||||
if x[0] == '@':
|
||||
# 符号引用初值
|
||||
# 符号引用初值(顶层 @name 现由 region 路径处理,这里是属性路径的兜底)
|
||||
_append_cstr(buf, size, x)
|
||||
return
|
||||
c0: int = x[0]
|
||||
@@ -677,8 +772,10 @@ def _MlirGlobalInit(buf: str, size: sizet,
|
||||
TypePrint(ty_buf, 512, gv.Ty, pool)
|
||||
_append_cstr(buf, size, ty_buf)
|
||||
return
|
||||
# { … } 结构体/vtable 初始化串等:响亮报错,绝不静默输出错误 MLIR
|
||||
MlirFail("MLIR 渲染: 全局初值形态 v1 不支持(结构体/vtable 初始化串需改用结构化初始化)")
|
||||
# 属性路径未覆盖且未走 region 的残留形态:响亮报错,绝不静默输出错误 MLIR
|
||||
# (结构体/vtable、含符号引用的聚合、指针型初值已由 _MlirGlobalInitRegion
|
||||
# 在 region 路径渲染,不再落到这里)
|
||||
MlirFail("MLIR 渲染: 全局初值形态 v1 不支持(属性路径与 initializer region 均未覆盖)")
|
||||
|
||||
|
||||
def _MlirCStringToMlir(buf: str, size: sizet, src: str, pool: memhub.MemBuddy*):
|
||||
@@ -748,6 +845,526 @@ def _MlirArrayDenseAt(buf: str, size: sizet, src: str,
|
||||
return p
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 全局初值的 initializer region 渲染(结构体/vtable 等聚合与指针初值)
|
||||
#
|
||||
# 实测 mlir-opt 22.1.8 通过形态(internal 与 internal constant 均可):
|
||||
# llvm.mlir.global constant @vt() : !llvm.struct<(!llvm.ptr, !llvm.ptr)> {
|
||||
# %v0 = llvm.mlir.addressof @"sha1.m" : !llvm.ptr
|
||||
# %undef = llvm.mlir.undef : !llvm.struct<(!llvm.ptr, !llvm.ptr)>
|
||||
# %s1 = llvm.insertvalue %v0, %undef[0] : !llvm.struct<(!llvm.ptr, !llvm.ptr)>
|
||||
# %v1 = llvm.mlir.zero : !llvm.ptr
|
||||
# %s2 = llvm.insertvalue %v1, %s1[1] : !llvm.struct<(!llvm.ptr, !llvm.ptr)>
|
||||
# llvm.return %s2 : !llvm.struct<(!llvm.ptr, !llvm.ptr)>
|
||||
# }
|
||||
#
|
||||
# 实现要点:
|
||||
# * 叶子 op(addressof / zero / constant)**先集中发射**,随后 undef 与
|
||||
# insertvalue 链;叶子 op 与插入 op 分写两个缓冲,最后按此顺序拼装;
|
||||
# * 每个 insertvalue 的**类型后缀始终是顶层聚合类型**,下标是"初值树 ×
|
||||
# 类型树"并行遍历得到的下标路径(嵌套聚合用多下标,如 [0, 1]);
|
||||
# * 元素 MLIR 类型来自**全局类型树**(Struct 字段链 / Array 元素类型)经
|
||||
# TypePrint 取文本;初值文本里的类型记号只用于推进游标,不作类型依据;
|
||||
# * bitcast/getelementptr 常量表达式整体视为"符号引用"——不透明指针下
|
||||
# bitcast 恒等、GEP 取首元素地址等价于 addressof,故不还原 bitcast;
|
||||
# * 符号名一律经 _MlirSymRefAppend(先剥 LLVM 口径引号,再按 MLIR 口径重加);
|
||||
# * 不支持的叶子形态一律 MlirFail(带原始文本),绝不静默输出错误 MLIR。
|
||||
# ============================================================
|
||||
GV_REGION_BUF_SIZE: t.CDefine = 32768
|
||||
|
||||
# region 渲染期状态(单线程递归渲染,逐全局重置)
|
||||
_MlirGvLeafBuf: str = None # 叶子 op 缓冲
|
||||
_MlirGvInsBuf: str = None # insertvalue 链缓冲
|
||||
_MlirGvTopType: str = None # 顶层聚合 MLIR 类型文本
|
||||
_MlirGvLast: str = None # 当前节点的 SSA 名
|
||||
_MlirGvPrev: str = None # insertvalue 链前一结果 SSA 名
|
||||
_MlirGvPool: memhub.MemBuddy* = None
|
||||
_MlirGvLeafSeq: int = 0
|
||||
_MlirGvInsSeq: int = 0
|
||||
|
||||
|
||||
def _MlirGlobalInitNeedsRegion(x: str) -> int:
|
||||
"""判定全局初值是否改走 initializer region(1=region / 0=属性路径)
|
||||
|
||||
属性路径(已实测通过,不得回退)仅覆盖: c 字符串、字符串字面量、
|
||||
标量字面量、**纯标量**数组。
|
||||
其余形态一律 region: **根级裸 `null` / `zeroinitializer`**、结构体 `{…}`、
|
||||
含符号引用(@)或结构体的数组、顶层裸符号引用 `@name`、指针/聚合类型前缀
|
||||
开头的初值(如 `i8* null`、`i8* bitcast (…)`、`i8* getelementptr …`)。
|
||||
"""
|
||||
if x is None or x[0] == '\0':
|
||||
return 0
|
||||
if x[0] == 'c' and x[1] == '"':
|
||||
return 0
|
||||
if x[0] == '"':
|
||||
return 0
|
||||
if x[0] == '[':
|
||||
return _MlirGvHasSymOrStruct(x)
|
||||
if x[0] == '{':
|
||||
return 1
|
||||
if string.strcmp(x, "null") == 0 or string.strcmp(x, "zeroinitializer") == 0:
|
||||
# 根级裸零值改走 region:属性形态 `(zeroinitializer)` 被 mlir-opt 拒绝
|
||||
# (实测 22.1.8: "expected attribute value");region 输出
|
||||
# `llvm.mlir.zero : <顶层类型>` + 直接 `llvm.return`。
|
||||
return 1
|
||||
if x[0] == '@':
|
||||
return 1
|
||||
c0: int = x[0]
|
||||
if c0 == '-' or (c0 >= '0' and c0 <= '9'):
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
def _MlirGvHasSymOrStruct(x: str) -> int:
|
||||
"""扫描初值文本(跳过字符串字面量): 出现符号引用 '@' 或结构体 '{' 返回 1"""
|
||||
p: str = x
|
||||
while p[0] != '\0':
|
||||
ch: int = p[0]
|
||||
if ch == '"':
|
||||
p = p + 1
|
||||
while p[0] != '\0' and p[0] != '"':
|
||||
# LLVM 字符串用 \xx 十六进制转义;'\' 跳过其后一字节
|
||||
if p[0] == 0x5C and p[1] != '\0':
|
||||
p = p + 1
|
||||
p = p + 1
|
||||
if p[0] == '"':
|
||||
p = p + 1
|
||||
continue
|
||||
if ch == '@' or ch == '{':
|
||||
return 1
|
||||
p = p + 1
|
||||
return 0
|
||||
|
||||
|
||||
def _MlirGlobalInitRegion(buf: str, size: sizet,
|
||||
pool: memhub.MemBuddy*, gv: rawptr[GlobalVariable]):
|
||||
"""渲染 initializer region 的体(2 空格缩进的行序列,不含最外层大括号)
|
||||
|
||||
每行都带 '\\n';调用方在末尾补 '}',模块层再统一缩进 2 空格。
|
||||
"""
|
||||
global _MlirGvLeafBuf, _MlirGvInsBuf, _MlirGvTopType, _MlirGvLast
|
||||
global _MlirGvPrev, _MlirGvPool, _MlirGvLeafSeq, _MlirGvInsSeq
|
||||
x: str = gv.Initializer
|
||||
if x is None:
|
||||
return
|
||||
topbuf: str = pool.alloc(1024)
|
||||
leafbuf: str = pool.alloc(GV_REGION_BUF_SIZE)
|
||||
insbuf: str = pool.alloc(GV_REGION_BUF_SIZE)
|
||||
lastbuf: str = pool.alloc(32)
|
||||
prevbuf: str = pool.alloc(32)
|
||||
if topbuf is None or leafbuf is None or insbuf is None \
|
||||
or lastbuf is None or prevbuf is None:
|
||||
MlirFail("MLIR 渲染: 全局 region 缓冲分配失败")
|
||||
topbuf[0] = '\0'
|
||||
leafbuf[0] = '\0'
|
||||
insbuf[0] = '\0'
|
||||
lastbuf[0] = '\0'
|
||||
prevbuf[0] = '\0'
|
||||
TypePrint(topbuf, 1024, gv.Ty, pool)
|
||||
|
||||
_MlirGvLeafBuf = leafbuf
|
||||
_MlirGvInsBuf = insbuf
|
||||
_MlirGvTopType = topbuf
|
||||
_MlirGvLast = lastbuf
|
||||
_MlirGvPrev = prevbuf
|
||||
_MlirGvPool = pool
|
||||
_MlirGvLeafSeq = 0
|
||||
_MlirGvInsSeq = 0
|
||||
|
||||
is_agg: int = 0
|
||||
match gv.Ty:
|
||||
case LLVMType.Struct(sf, sfc, sn):
|
||||
is_agg = 1
|
||||
case LLVMType.Array(ae, ac):
|
||||
is_agg = 1
|
||||
case _:
|
||||
is_agg = 0
|
||||
if is_agg != 0:
|
||||
# insertvalue 链的起点: 顶层 undef
|
||||
string.strcpy(prevbuf, "%undef")
|
||||
|
||||
_MlirGvWalk(x, gv.Ty, "", 0)
|
||||
|
||||
# 叶子 op 集中发射,再 undef + insertvalue 链
|
||||
_append_cstr(buf, size, leafbuf)
|
||||
if is_agg != 0:
|
||||
_append_cstr(buf, size, " %undef = llvm.mlir.undef : ")
|
||||
_append_cstr(buf, size, topbuf)
|
||||
_append_cstr(buf, size, "\n")
|
||||
_append_cstr(buf, size, insbuf)
|
||||
_append_cstr(buf, size, " llvm.return ")
|
||||
if lastbuf[0] != '\0':
|
||||
_append_cstr(buf, size, lastbuf)
|
||||
else:
|
||||
_append_cstr(buf, size, "%undef")
|
||||
_append_cstr(buf, size, " : ")
|
||||
_append_cstr(buf, size, topbuf)
|
||||
_append_cstr(buf, size, "\n")
|
||||
|
||||
|
||||
def _MlirGvWalk(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str:
|
||||
"""并行遍历"初值树 × 类型树"的一个节点,返回新游标
|
||||
|
||||
结果 SSA 名写入 _MlirGvLast(叶子=物化名;聚合=最后一个 insertvalue 名;
|
||||
空聚合= %undef)。下标路径 path 形如 "" / "0" / "0, 1"。
|
||||
"""
|
||||
if depth > 64:
|
||||
MlirFail("MLIR 渲染: 全局初值嵌套过深(> 64 层)")
|
||||
if ty is None:
|
||||
MlirFail("MLIR 渲染: 全局初值类型树缺失(无法确定元素 MLIR 类型)")
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == '\0':
|
||||
MlirFail("MLIR 渲染: 全局初值文本意外结束")
|
||||
# 显式数组类型前缀(如字段写成 "[3 x i8] [i8 1, i8 2]")先整体读掉
|
||||
if p[0] == '[' and _MlirGvIsArrayType(p) != 0:
|
||||
tb: str = _MlirGvPool.alloc(512)
|
||||
if tb is None:
|
||||
MlirFail("MLIR 渲染: 数组类型前缀缓冲分配失败")
|
||||
p = _mlir_read_type(p, tb, 512)
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == '{':
|
||||
return _MlirGvWalkStruct(p, ty, path, depth)
|
||||
if p[0] == '[':
|
||||
return _MlirGvWalkArray(p, ty, path, depth)
|
||||
return _MlirGvWalkLeaf(p, ty, path, depth)
|
||||
|
||||
|
||||
def _MlirGvIsArrayType(p: str) -> int:
|
||||
"""判断游标处是否为 LLVM 数组类型前缀 "[N x TY]"(用于与数组字面量区分)"""
|
||||
if p is None or p[0] != '[':
|
||||
return 0
|
||||
q: str = p + 1
|
||||
nd: int = 0
|
||||
while q[0] >= '0' and q[0] <= '9':
|
||||
q = q + 1
|
||||
nd += 1
|
||||
if nd == 0 or q[0] != ' ':
|
||||
return 0
|
||||
q = q + 1
|
||||
if q[0] != 'x' or q[1] != ' ':
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
def _MlirGvPathChild(path: str, idx: int) -> str:
|
||||
"""由父下标路径与子序号拼出子下标路径(顶层 path="" → "0")"""
|
||||
nb: str = _MlirGvPool.alloc(string.strlen(path) + 16)
|
||||
if nb is None:
|
||||
MlirFail("MLIR 渲染: 下标路径缓冲分配失败")
|
||||
if path[0] == '\0':
|
||||
viperlib.snprintf(nb, string.strlen(path) + 16, "%d", idx)
|
||||
else:
|
||||
viperlib.snprintf(nb, string.strlen(path) + 16, "%s, %d", path, idx)
|
||||
return nb
|
||||
|
||||
|
||||
def _MlirGvWalkStruct(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str:
|
||||
"""结构体初值 { 元素, 元素, … }: 字段类型取类型树的 Struct 字段链"""
|
||||
fields: rawptr[ParamNode] = None
|
||||
match ty:
|
||||
case LLVMType.Struct(sf, sfc, sn):
|
||||
fields = sf
|
||||
case _:
|
||||
MlirFail("MLIR 渲染: 初值为结构体,但全局类型不是结构体")
|
||||
cur: rawptr[ParamNode] = fields
|
||||
p = p + 1
|
||||
idx: int = 0
|
||||
done: int = 0
|
||||
guard: int = 0
|
||||
while done == 0:
|
||||
guard += 1
|
||||
if guard > 4096:
|
||||
MlirFail("MLIR 渲染: 结构体初值元素过多")
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == '\0':
|
||||
MlirFail("MLIR 渲染: 结构体初值缺少 '}'")
|
||||
if p[0] == '}':
|
||||
p = p + 1
|
||||
done = 1
|
||||
elif p[0] == ',':
|
||||
p = p + 1
|
||||
else:
|
||||
fty: rawptr[LLVMType] = None
|
||||
if cur is not None:
|
||||
fty = param_get_ty(cur)
|
||||
p = _MlirGvWalk(p, fty, _MlirGvPathChild(path, idx), depth + 1)
|
||||
if cur is not None:
|
||||
cur = param_get_next(cur)
|
||||
idx += 1
|
||||
if idx == 0:
|
||||
string.strcpy(_MlirGvLast, "%undef")
|
||||
return p
|
||||
|
||||
|
||||
def _MlirGvWalkArray(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str:
|
||||
"""数组初值 [ 元素, 元素, … ]: 元素类型取类型树的 Array 元素类型"""
|
||||
elem_ty: rawptr[LLVMType] = None
|
||||
match ty:
|
||||
case LLVMType.Array(ae, ac):
|
||||
elem_ty = ae
|
||||
case _:
|
||||
MlirFail("MLIR 渲染: 初值为数组,但全局类型不是数组")
|
||||
p = p + 1
|
||||
idx: int = 0
|
||||
done: int = 0
|
||||
guard: int = 0
|
||||
while done == 0:
|
||||
guard += 1
|
||||
if guard > 65536:
|
||||
MlirFail("MLIR 渲染: 数组初值元素过多")
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == '\0':
|
||||
MlirFail("MLIR 渲染: 数组初值缺少 ']'")
|
||||
if p[0] == ']':
|
||||
p = p + 1
|
||||
done = 1
|
||||
elif p[0] == ',':
|
||||
p = p + 1
|
||||
else:
|
||||
p = _MlirGvWalk(p, elem_ty, _MlirGvPathChild(path, idx), depth + 1)
|
||||
idx += 1
|
||||
if idx == 0:
|
||||
string.strcpy(_MlirGvLast, "%undef")
|
||||
return p
|
||||
|
||||
|
||||
def _MlirGvIsBareValue(p: str) -> int:
|
||||
"""游标处是否**直接**是值(无 LLVM/MLIR 类型前缀)
|
||||
|
||||
TPV 的根初值可能是无类型前缀的裸形态(如字符串指针全局的
|
||||
`getelementptr inbounds ([N x i8], … @.str.x …)`、对象 storage 引用 `@name`);
|
||||
聚合元素则通常是 `<类型> <值>`。类型记号从不以 `@`/数字/`-`/`.` 开头,
|
||||
也不会有 `bitcast`/`getelementptr`/`null`/`zeroinitializer` 这些名字,
|
||||
故该判据无歧义。
|
||||
"""
|
||||
if p is None or p[0] == '\0':
|
||||
return 0
|
||||
if p[0] == '@':
|
||||
return 1
|
||||
if string.strncmp(p, "bitcast", 7) == 0:
|
||||
return 1
|
||||
if string.strncmp(p, "getelementptr", 13) == 0:
|
||||
return 1
|
||||
if string.strncmp(p, "null", 4) == 0:
|
||||
return 1
|
||||
if string.strncmp(p, "zeroinitializer", 15) == 0:
|
||||
return 1
|
||||
c0: int = p[0]
|
||||
if c0 == '-' or (c0 >= '0' and c0 <= '9') or c0 == '.':
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _MlirGvWalkLeaf(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str:
|
||||
"""叶子元素 `<LLVM 类型> <值>`: 值解析为 符号引用 / null / 标量字面量
|
||||
|
||||
元素 MLIR 类型不取初值文本里的类型记号,而取类型树(ty)。
|
||||
顶层叶子(depth == 0,整棵初值即该叶子,如指针型全局)不走 insertvalue
|
||||
链;只有位于聚合内部的叶子才需要插入。
|
||||
"""
|
||||
p = _mlir_ws(p)
|
||||
if _MlirGvIsBareValue(p) == 0:
|
||||
# 元素自带类型记号(`i8* null` / `!llvm.ptr null` 等):只用于推进游标
|
||||
tb: str = _MlirGvPool.alloc(512)
|
||||
if tb is None:
|
||||
MlirFail("MLIR 渲染: 叶子类型记号缓冲分配失败")
|
||||
p = _mlir_read_type(p, tb, 512)
|
||||
p = _mlir_ws(p)
|
||||
if p[0] == '\0':
|
||||
MlirFail("MLIR 渲染: 叶子初值只有类型、缺少值")
|
||||
if p[0] == '@':
|
||||
# 裸符号引用(剥 LLVM 引号 → 经 _MlirSymRefAppend 按 MLIR 口径重加)
|
||||
nb: str = _MlirGvPool.alloc(512)
|
||||
if nb is None:
|
||||
MlirFail("MLIR 渲染: 符号名缓冲分配失败")
|
||||
p = _MlirGvReadSymName(p, nb, 512)
|
||||
_MlirGvEmitAddressOf(nb)
|
||||
elif string.strncmp(p, "bitcast", 7) == 0 \
|
||||
or string.strncmp(p, "getelementptr", 13) == 0:
|
||||
# 常量表达式整体视为符号引用(bitcast 不透明指针下恒等;GEP 取首元素地址)
|
||||
nb2: str = _MlirGvPool.alloc(512)
|
||||
if nb2 is None:
|
||||
MlirFail("MLIR 渲染: 常量表达式符号名缓冲分配失败")
|
||||
p = _MlirGvScanConstExprSym(p, nb2, 512)
|
||||
_MlirGvEmitAddressOf(nb2)
|
||||
elif string.strncmp(p, "null", 4) == 0:
|
||||
_MlirGvEmitZero(ty)
|
||||
p = p + 4
|
||||
elif string.strncmp(p, "zeroinitializer", 15) == 0:
|
||||
_MlirGvEmitZero(ty)
|
||||
p = p + 15
|
||||
else:
|
||||
c0: int = p[0]
|
||||
if c0 == '-' or (c0 >= '0' and c0 <= '9') or c0 == '.':
|
||||
vb: str = _MlirGvPool.alloc(512)
|
||||
if vb is None:
|
||||
MlirFail("MLIR 渲染: 标量初值缓冲分配失败")
|
||||
p = _mlir_read_value(p, vb, 512)
|
||||
_MlirGvEmitConst(vb, ty)
|
||||
else:
|
||||
# 不支持的叶子形态: 带原始文本响亮报错,绝不静默产错
|
||||
emb: str = _MlirGvPool.alloc(string.strlen(p) + 128)
|
||||
if emb is not None:
|
||||
viperlib.snprintf(emb, string.strlen(p) + 128,
|
||||
"MLIR 渲染: 全局初值叶子形态 v1 不支持:'%s'", p)
|
||||
MlirFail(emb)
|
||||
MlirFail("MLIR 渲染: 全局初值叶子形态 v1 不支持")
|
||||
if depth > 0:
|
||||
_MlirGvInsertLeaf(path)
|
||||
return p
|
||||
|
||||
|
||||
def _MlirGvScanConstExprSym(p: str, out: str, outsz: sizet) -> str:
|
||||
"""扫描 `bitcast (…)` / `getelementptr … (…)`: 在括号组内提取首个 '@' 符号名
|
||||
(剥引号写入 out),返回括号组之后的游标"""
|
||||
q: str = p
|
||||
while q[0] != '\0' and q[0] != '(':
|
||||
q = q + 1
|
||||
if q[0] != '(':
|
||||
MlirFail("MLIR 渲染: 常量表达式缺少 '('(bitcast/getelementptr)")
|
||||
dep: int = 0
|
||||
at: str = None
|
||||
guard: int = 0
|
||||
while q[0] != '\0':
|
||||
guard += 1
|
||||
if guard > 4096:
|
||||
MlirFail("MLIR 渲染: 常量表达式过长")
|
||||
ch: int = q[0]
|
||||
if ch == '(':
|
||||
dep += 1
|
||||
elif ch == ')':
|
||||
dep -= 1
|
||||
if dep == 0:
|
||||
q = q + 1
|
||||
break
|
||||
elif ch == '@' and at is None:
|
||||
at = q
|
||||
q = q + 1
|
||||
if at is None:
|
||||
MlirFail("MLIR 渲染: 常量表达式中未找到符号引用 '@'")
|
||||
_MlirGvReadSymName(at, out, outsz)
|
||||
return q
|
||||
|
||||
|
||||
def _MlirGvReadSymName(at: str, out: str, outsz: sizet) -> str:
|
||||
"""从 '@' 处读符号名,剥掉 LLVM 口径引号("…"),返回名字之后的游标"""
|
||||
if at is None or at[0] != '@':
|
||||
MlirFail("MLIR 渲染: 期望符号引用('@')")
|
||||
q: str = at + 1
|
||||
n: sizet = 0
|
||||
if q[0] == '"':
|
||||
q = q + 1
|
||||
while q[0] != '\0' and q[0] != '"':
|
||||
if n + 1 < outsz:
|
||||
out[n] = q[0]
|
||||
n += 1
|
||||
q = q + 1
|
||||
if q[0] == '"':
|
||||
q = q + 1
|
||||
else:
|
||||
while q[0] != '\0' and _MlirGvSymNameEnd(q[0]) == 0:
|
||||
if n + 1 < outsz:
|
||||
out[n] = q[0]
|
||||
n += 1
|
||||
q = q + 1
|
||||
out[n] = '\0'
|
||||
if n == 0:
|
||||
MlirFail("MLIR 渲染: 空符号名")
|
||||
return q
|
||||
|
||||
|
||||
def _MlirGvSymNameEnd(ch: int) -> int:
|
||||
"""裸符号名的终止字符(空白 / ',' / ')' / ']' / '}')"""
|
||||
if ch == 0 or ch == 32 or ch == 9 or ch == 10 or ch == 13:
|
||||
return 1
|
||||
if ch == ',' or ch == ')' or ch == ']' or ch == '}':
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _MlirGvNextLeafName() -> str:
|
||||
"""分配下一个叶子 SSA 名(%v0, %v1, …)"""
|
||||
global _MlirGvLeafSeq
|
||||
ln: str = _MlirGvPool.alloc(32)
|
||||
if ln is None:
|
||||
MlirFail("MLIR 渲染: 叶子 SSA 名缓冲分配失败")
|
||||
viperlib.snprintf(ln, 32, "%%v%d", _MlirGvLeafSeq)
|
||||
_MlirGvLeafSeq += 1
|
||||
return ln
|
||||
|
||||
|
||||
def _MlirGvEmitAddressOf(name: str):
|
||||
"""发射叶子 op: %vN = llvm.mlir.addressof @"name" : !llvm.ptr"""
|
||||
ln: str = _MlirGvNextLeafName()
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " ")
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ln)
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " = llvm.mlir.addressof ")
|
||||
_MlirSymRefAppend(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, name)
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " : !llvm.ptr\n")
|
||||
string.strcpy(_MlirGvLast, ln)
|
||||
|
||||
|
||||
def _MlirGvEmitZero(ty: rawptr[LLVMType]):
|
||||
"""发射叶子 op: %vN = llvm.mlir.zero : <元素 MLIR 类型>"""
|
||||
mt: str = _MlirGvPool.alloc(512)
|
||||
if mt is None:
|
||||
MlirFail("MLIR 渲染: 零初值类型缓冲分配失败")
|
||||
mt[0] = '\0'
|
||||
TypePrint(mt, 512, ty, _MlirGvPool)
|
||||
ln: str = _MlirGvNextLeafName()
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " ")
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ln)
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " = llvm.mlir.zero : ")
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, mt)
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, "\n")
|
||||
string.strcpy(_MlirGvLast, ln)
|
||||
|
||||
|
||||
def _MlirGvEmitConst(val: str, ty: rawptr[LLVMType]):
|
||||
"""发射叶子 op: %vN = llvm.mlir.constant(<lit> : <元素 MLIR 类型>) : <同类型>"""
|
||||
mt: str = _MlirGvPool.alloc(512)
|
||||
if mt is None:
|
||||
MlirFail("MLIR 渲染: 标量元素类型缓冲分配失败")
|
||||
mt[0] = '\0'
|
||||
TypePrint(mt, 512, ty, _MlirGvPool)
|
||||
if mt[0] != 'i' and mt[0] != 'f':
|
||||
MlirFail("MLIR 渲染: 全局初值标量元素类型既非整数也非浮点")
|
||||
ln: str = _MlirGvNextLeafName()
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " ")
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ln)
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " = llvm.mlir.constant(")
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, val)
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " : ")
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, mt)
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ") : ")
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, mt)
|
||||
_append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, "\n")
|
||||
string.strcpy(_MlirGvLast, ln)
|
||||
|
||||
|
||||
def _MlirGvInsertLeaf(path: str):
|
||||
"""发射 insertvalue(多下标路径;类型后缀始终是顶层聚合类型)"""
|
||||
global _MlirGvInsSeq
|
||||
_MlirGvInsSeq += 1
|
||||
sn: str = _MlirGvPool.alloc(32)
|
||||
if sn is None:
|
||||
MlirFail("MLIR 渲染: insertvalue SSA 名缓冲分配失败")
|
||||
viperlib.snprintf(sn, 32, "%%s%d", _MlirGvInsSeq)
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, " ")
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, sn)
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, " = llvm.insertvalue ")
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, _MlirGvLast)
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, ", ")
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, _MlirGvPrev)
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, "[")
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, path)
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, "] : ")
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, _MlirGvTopType)
|
||||
_append_cstr(_MlirGvInsBuf, GV_REGION_BUF_SIZE, "\n")
|
||||
string.strcpy(_MlirGvPrev, sn)
|
||||
string.strcpy(_MlirGvLast, sn)
|
||||
|
||||
|
||||
def _MlirAppendIndented(dst: str, dst_size: sizet, src: str,
|
||||
indent: int, pool: memhub.MemBuddy*):
|
||||
"""把 src 的每一行前加 indent 个空格后追加到 dst(模块成员缩进)"""
|
||||
|
||||
+351
-20
@@ -74,11 +74,26 @@ class LLVMType(t.REnum):
|
||||
# ============================================================
|
||||
# 内部分配辅助
|
||||
# ============================================================
|
||||
def _TypeSizeGuard() -> sizet:
|
||||
"""LLVMType 的字节尺寸;为 0 时响亮失败。
|
||||
|
||||
__sizeof__() 在布局未就绪(结构体未注册 / opaque / 字段链未填充)时被
|
||||
算成 0,此时 alloc(0) 只会静默返回 NULL,类型对象随之丢失且无从追查。
|
||||
"""
|
||||
sz: sizet = LLVMType.__sizeof__()
|
||||
if sz == 0:
|
||||
stdio.printf("FATAL: mlirlite.LLVMType.__sizeof__() == 0"
|
||||
"(布局未注册或 opaque),拒绝 alloc(0)\n")
|
||||
stdlib.exit(1)
|
||||
return sz
|
||||
|
||||
|
||||
def _new_type(pool: memhub.MemBuddy*) -> rawptr[LLVMType]:
|
||||
"""从 mpool 分配一个零初始化的 LLVMType"""
|
||||
ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__())
|
||||
sz_nt: sizet = _TypeSizeGuard()
|
||||
ptr: rawptr[LLVMType] = pool.alloc(sz_nt)
|
||||
if ptr:
|
||||
string.memset(ptr, 0, LLVMType.__sizeof__())
|
||||
string.memset(ptr, 0, sz_nt)
|
||||
return ptr
|
||||
|
||||
|
||||
@@ -380,13 +395,260 @@ def MlirFail(msg: str):
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 说明:本库**不需要**命名结构体类型登记表。
|
||||
# 渲染上下文行:类型文本转换失败时随报文一并打印
|
||||
#
|
||||
# builders 未改,其内嵌类型一律经 _type_str → TypePrint 生成,因此中间指令
|
||||
# 文本里出现的结构体类型已经是完整的 MLIR 内联形态(!llvm.struct<(…)>),
|
||||
# 不含 %"name" 这种"只有名字"的形态;指针也统一收敛为 !llvm.ptr。
|
||||
# 若出现 %"name"(手工构造的中间文本),MlirTypeText 会响亮报错。
|
||||
# MlirFail 是即时终止,拿不到调用栈。故由渲染侧(__function.vp 的
|
||||
# _MlirRenderLine 入口)把"当前正在渲染的中间指令文本行"寄存到本模块,
|
||||
# 类型转换失败报文再把它追加出来,一次定位到出错的那一行中间文本。
|
||||
# 依赖方向保持单向:本模块只存/读,不反向 import __function.vp。
|
||||
# ============================================================
|
||||
_MlirCtxLine: str = None
|
||||
|
||||
|
||||
def MlirSetContextLine(line: str = None):
|
||||
"""寄存当前正在渲染的中间文本行(无行时传 None)"""
|
||||
global _MlirCtxLine
|
||||
_MlirCtxLine = line
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 命名结构体(%"name" = type {…})在 MLIR 的表示
|
||||
#
|
||||
# MLIR(llvm 方言) **没有独立类型定义语法**(故 llvmlite 的
|
||||
# `%"name" = type {…}` 定义行在 MLIR 侧**整体丢弃**,见 __module.vp 的
|
||||
# PrintModule:NamedTypeHead 只作模型保留、不产生输出),也**不允许无体
|
||||
# 前向声明**(实测 mlir-opt 22.1.8:`!llvm.struct<"x">` 报
|
||||
# "struct without a body only allowed in a recursive struct")。
|
||||
# 故唯一可表达形态是**使用处的内联具名体**:
|
||||
#
|
||||
# !llvm.struct<"名字", (字段类型…)>
|
||||
#
|
||||
# 四种形态均由 mlir-opt / mlir-translate 22.1.8 实测 exit 0(逐字照此):
|
||||
# 按值 : !llvm.struct<"a.b", (i32, !llvm.ptr)>
|
||||
# 特殊字符 : !llvm.struct<"01c70390f01048df.Lock[0]", (i32)>
|
||||
# (名字含数字/`.`/`[`/`]` 直接作字符串字面量,**不额外转义/加引号**)
|
||||
# 嵌套具名 : !llvm.struct<"A.Lock", (!llvm.struct<"A.CRIT", (i32)>)>
|
||||
# 空体 : !llvm.struct<"x.Empty", ()>
|
||||
# 降级证据:mlir-translate --mlir-to-llvmir 会由使用处的内联形态**自动**生成
|
||||
# 定义行(实测得 `%aa11bb22cc33dd44.Cls = type { i32, ptr }` 等,见 README §2.10)。
|
||||
# 指向命名结构体的指针仍是 !llvm.ptr(不透明指针,既有路径不变)。
|
||||
#
|
||||
# 边界(一律响亮报错,绝不静默输出错误类型):
|
||||
# * fcount < 0(opaque 哨兵,跨模块前向声明)⇒ MLIR 无法表达无体结构体;
|
||||
# * 名字含 `"` / `\` / 控制字符 ⇒ MLIR 字符串字面量非法字符;
|
||||
# * 名字为空、或具名而字段链缺失(FCount 非 0 但 Fields == None)⇒ 模型不自洽。
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 命名结构体登记表(模块打印期由 __module.vp 发布;供"只有名字"的类型文本回查)
|
||||
#
|
||||
# 中间文本里的 `%"name"`(只有名字的结构体引用:手工构造 / TPV 注入)本身不含
|
||||
# 字段,而 MLIR 又不允许无体前向声明 ⇒ 只有回查**模块里已登记的完整定义**才能
|
||||
# 展开为使用处的内联具名形态。__module.vp 的 PrintModule 在打印开始时按
|
||||
# NamedTypeHead 重建本表(module_add_named_type 已保证同名节点至多一项、且
|
||||
# opaque 会被后到的完整定义就地替换),打印结束时清空 —— 表内类型指针只在
|
||||
# 当次打印期间有效,清空后不再解引用(避免 pool reset 后悬空)。
|
||||
# 表按当次模块的具名类型**实际数量**精确分配(无具名类型则零开销)。
|
||||
# ============================================================
|
||||
_MlirNamedTys: sizet* = None # 槽位数组:每槽 16 字节 = { LLVMType*, IsPacked }
|
||||
_MlirNamedCount: int = 0 # 已登记项数
|
||||
_MlirNamedCap: sizet = 0 # 槽位容量(= 当次模块的具名类型数)
|
||||
|
||||
# 槽位布局: [0] = LLVMType*(命名结构体类型);[1] = IsPacked(1=packed)。
|
||||
# packed 标记只存在于 NamedTypeNode 上(LLVMType.Struct 变体没有该字段,
|
||||
# 匿名结构体结构上无法携带),设置点唯一:module_set_named_type_packed。
|
||||
# 故登记时由 __module.vp 的 _MlirPublishNamedTypes 从节点读出后一并存入本表,
|
||||
# 渲染侧(TypePrint 具名分支 / 文本→文本具名路径)只从本表取标记。
|
||||
|
||||
|
||||
def _MlirNamedSlot(idx: int) -> sizet*:
|
||||
"""登记表槽位地址(数组基址 + idx*16)"""
|
||||
return (sizet*)(t.CPtr(uint64(_MlirNamedTys) + uint64(idx * 16)))
|
||||
|
||||
|
||||
def MlirNamedTypesBegin(pool: memhub.MemBuddy*, capacity: sizet):
|
||||
"""按模块的具名结构体**实际数量**分配登记表并清空计数(模块打印开始时调用)
|
||||
|
||||
按需精确分配(TPV 真实模块里具名类型可达数千个,见 README §5.9),
|
||||
capacity == 0 时不分配(无具名类型的模块零开销)。
|
||||
"""
|
||||
global _MlirNamedTys, _MlirNamedCount, _MlirNamedCap
|
||||
_MlirNamedTys = None
|
||||
_MlirNamedCount = 0
|
||||
_MlirNamedCap = 0
|
||||
if capacity <= 0:
|
||||
return
|
||||
_MlirNamedTys = pool.alloc(capacity * 16)
|
||||
if _MlirNamedTys is None:
|
||||
MlirFail("MLIR 渲染: 命名结构体登记表分配失败")
|
||||
_MlirNamedCap = capacity
|
||||
|
||||
|
||||
def MlirNamedTypesClear():
|
||||
"""清空登记表(模块打印结束时调用;表内类型指针此后不再解引用)"""
|
||||
global _MlirNamedTys, _MlirNamedCount, _MlirNamedCap
|
||||
_MlirNamedTys = None
|
||||
_MlirNamedCount = 0
|
||||
_MlirNamedCap = 0
|
||||
|
||||
|
||||
def MlirNamedTypeRegister(ty: rawptr[LLVMType], is_packed: int):
|
||||
"""登记一个命名结构体类型及其 packed 标记(同名:完整定义覆盖 opaque 声明)
|
||||
|
||||
与 module_add_named_type 的去重口径一致:opaque 声明不覆盖已有的完整定义。
|
||||
非命名结构体(Name 为 None)忽略;超出容量(理论不可达,容量取自链表长度)
|
||||
则停止登记 —— 查不到 ⇒ 响亮报错,不静默产出错误类型。
|
||||
is_packed 取自 NamedTypeNode.IsPacked(packed 标记只在具名节点上存在,
|
||||
匿名结构体结构上无法携带,见 README §2.10)。
|
||||
"""
|
||||
global _MlirNamedCount
|
||||
if ty is None or _MlirNamedTys is None:
|
||||
return
|
||||
name: str = get_struct_name(ty)
|
||||
if name is None:
|
||||
return
|
||||
fcount: int = get_struct_fcount(ty)
|
||||
i: int = 0
|
||||
while i < _MlirNamedCount:
|
||||
slot: sizet* = _MlirNamedSlot(i)
|
||||
ex: rawptr[LLVMType] = (rawptr[LLVMType])(slot[0])
|
||||
ex_name: str = get_struct_name(ex)
|
||||
if ex_name is not None and string.strcmp(ex_name, name) == 0:
|
||||
if fcount < 0 and get_struct_fcount(ex) >= 0:
|
||||
return
|
||||
slot[0] = sizet(ty)
|
||||
slot[1] = sizet(is_packed)
|
||||
return
|
||||
i += 1
|
||||
if _MlirNamedCount >= int(_MlirNamedCap):
|
||||
return
|
||||
slot2: sizet* = _MlirNamedSlot(_MlirNamedCount)
|
||||
slot2[0] = sizet(ty)
|
||||
slot2[1] = sizet(is_packed)
|
||||
_MlirNamedCount += 1
|
||||
|
||||
|
||||
def _MlirNamedFindSlot(name: str) -> int:
|
||||
"""按名字回查已登记槽位下标(未登记 / 表未发布 ⇒ -1)"""
|
||||
if name is None or _MlirNamedTys is None:
|
||||
return -1
|
||||
i: int = 0
|
||||
while i < _MlirNamedCount:
|
||||
slot: sizet* = _MlirNamedSlot(i)
|
||||
ty: rawptr[LLVMType] = (rawptr[LLVMType])(slot[0])
|
||||
nm: str = get_struct_name(ty)
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
return i
|
||||
i += 1
|
||||
return -1
|
||||
|
||||
|
||||
def _MlirNamedPackedOf(name: str) -> int:
|
||||
"""按名字回查登记表里的 packed 标记(未登记 / 表未发布 ⇒ 0)
|
||||
|
||||
TypePrint 的具名分支只有内部模型的 Name(LLVMType.Struct 无 packed 字段),
|
||||
标记经此从登记表取,与文本→文本路径同源;故 packed 只在模块打印期
|
||||
(登记表已发布)可见,独立 TypePrint 调用无表即视为非 packed。
|
||||
"""
|
||||
si: int = _MlirNamedFindSlot(name)
|
||||
if si < 0:
|
||||
return 0
|
||||
slot: sizet* = _MlirNamedSlot(si)
|
||||
return int(slot[1])
|
||||
|
||||
|
||||
def _MlirFailNamed(pool: memhub.MemBuddy*, reason: str, name: str):
|
||||
"""带类型名 + 当前中间文本行的响亮报错
|
||||
|
||||
MlirFail 是即时终止、拿不到调用栈(见 README §5.8),故把定位信息备齐:
|
||||
类型名 + 当前正在渲染的中间文本行(_MlirCtxLine,由渲染侧寄存)。
|
||||
"""
|
||||
if name is None or name[0] == '\0':
|
||||
MlirFail(reason)
|
||||
nlen: sizet = string.strlen(name)
|
||||
rlen: sizet = string.strlen(reason)
|
||||
clen: sizet = 0
|
||||
if _MlirCtxLine is not None:
|
||||
clen = string.strlen(_MlirCtxLine)
|
||||
bsize: sizet = nlen + rlen + clen + 64
|
||||
buf: str = pool.alloc(bsize)
|
||||
if buf is None:
|
||||
MlirFail(reason)
|
||||
if _MlirCtxLine is not None:
|
||||
viperlib.snprintf(buf, bsize, "%s(类型名 '%s') [行: %s]",
|
||||
reason, name, _MlirCtxLine)
|
||||
else:
|
||||
viperlib.snprintf(buf, bsize, "%s(类型名 '%s')", reason, name)
|
||||
MlirFail(buf)
|
||||
|
||||
|
||||
def _MlirNamedCheckName(pool: memhub.MemBuddy*, name: str):
|
||||
"""校验名字可作 MLIR 字符串字面量内容(`"` / `\\` / 控制字符 ⇒ 响亮报错)"""
|
||||
if name is None or name[0] == '\0':
|
||||
MlirFail("MLIR 渲染: 命名结构体名字为空")
|
||||
i: sizet = 0
|
||||
while name[i] != '\0':
|
||||
ch: int = name[i]
|
||||
if ch == '"' or ch == 0x5C or (ch > 0 and ch < 32):
|
||||
_MlirFailNamed(pool,
|
||||
"MLIR 渲染: 命名结构体名字含 MLIR 字符串字面量非法字符(\" / \\ / 控制字符)",
|
||||
name)
|
||||
i += 1
|
||||
|
||||
|
||||
def _MlirNamedStructAppend(dst: str, dstsz: sizet, name: str,
|
||||
fields: rawptr[ParamNode], fcount: int, packed: int,
|
||||
pool: memhub.MemBuddy*, depth: int):
|
||||
"""追加 MLIR 具名结构体类型文本(追加写 dst)
|
||||
|
||||
非 packed: `!llvm.struct<"name", (字段…)>`;
|
||||
packed: `!llvm.struct<"name", packed (字段…)>` —— 实测 mlir-opt /
|
||||
mlir-translate 22.1.8 exit 0,降级为 `%"name" = type <{ … }>`(见 README §2.10)。
|
||||
MLIR 只在**具名**形态下支持该关键字(匿名 `{…}` 对应 `!llvm.struct<(...)>`,
|
||||
没有关键字位);而内部模型的 packed 标记只挂在 NamedTypeNode 上
|
||||
(LLVMType.Struct 变体无该字段),匿名结构体结构上无法携带 packed。
|
||||
|
||||
opaque(fcount < 0)与"具名但字段链缺失"一律响亮报错:MLIR 无法表达无体
|
||||
结构体,静默输出空体(`!llvm.struct<"x", ()>`)会得到**错误布局**。
|
||||
"""
|
||||
if dst is None or dstsz == 0:
|
||||
return
|
||||
_MlirNamedCheckName(pool, name)
|
||||
if fcount < 0:
|
||||
_MlirFailNamed(pool,
|
||||
"MLIR 渲染: 命名结构体在打印时仍为 opaque(无字段体)——MLIR 不允许无体前向声明",
|
||||
name)
|
||||
if fields is None and fcount != 0:
|
||||
_MlirFailNamed(pool,
|
||||
"MLIR 渲染: 命名结构体字段链缺失(FCount 非 0 但无字段)",
|
||||
name)
|
||||
_strappend(dst, dstsz, "!llvm.struct<\"")
|
||||
_strappend(dst, dstsz, name)
|
||||
_strappend(dst, dstsz, "\", ")
|
||||
if packed != 0:
|
||||
_strappend(dst, dstsz, "packed ")
|
||||
_strappend(dst, dstsz, "(")
|
||||
cur: rawptr[ParamNode] = fields
|
||||
first: int = 1
|
||||
fidx: int = 0
|
||||
tmp: str = pool.alloc(512)
|
||||
while cur is not None:
|
||||
# 悬空保护:字段链成环/内容复用时限制节点数并**响亮报错**——截断(llvmlite
|
||||
# 匿名路径的 "...")会得到静默错误的布局,故具名路径不做截断。
|
||||
if fidx >= 4096:
|
||||
_MlirFailNamed(pool,
|
||||
"MLIR 渲染: 命名结构体字段过多或字段链成环(> 4096)",
|
||||
name)
|
||||
if first == 0:
|
||||
_strappend(dst, dstsz, ", ")
|
||||
if tmp is not None and cur.Ty is not None:
|
||||
_MlirTypePrintDepth(tmp, 512, cur.Ty, pool, depth + 1)
|
||||
_strappend(dst, dstsz, tmp)
|
||||
cur = cur.Next
|
||||
first = 0
|
||||
fidx += 1
|
||||
_strappend(dst, dstsz, ")>")
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -561,15 +823,25 @@ def _MlirTypeAppend(dst: str, dstsz: sizet, src: str, pool: memhub.MemBuddy*,
|
||||
if fp != 0:
|
||||
_MlirFuncText(dst, dstsz, p, lt, fp, pool, depth)
|
||||
return
|
||||
# 报文带上原始类型文本(截断到 lt),便于一次定位:
|
||||
# 若谓词/记号被误当类型,会直接看到 'sgt' 这类内容。
|
||||
# 报文带上原始类型文本(截断到 lt)+ 当前正在渲染的中间文本行,便于一次定位:
|
||||
# 若谓词/记号被误当类型,会直接看到 'sgt' 这类内容;行上下文由
|
||||
# MlirSetContextLine 寄存(见本文件顶部的 _MlirCtxLine)。
|
||||
emb: str = pool.alloc(lt + 1)
|
||||
if emb is not None:
|
||||
string.strncpy(emb, p, lt)
|
||||
emb[lt] = '\0'
|
||||
emsg: str = pool.alloc(lt + 128)
|
||||
clen: sizet = 0
|
||||
if _MlirCtxLine is not None:
|
||||
clen = string.strlen(_MlirCtxLine)
|
||||
emsg: str = pool.alloc(lt + clen + 128)
|
||||
if emsg is not None:
|
||||
viperlib.snprintf(emsg, lt + 128, "无法转换为 MLIR 类型文本(v1 未覆盖该形态):'%s'", emb)
|
||||
if _MlirCtxLine is not None:
|
||||
viperlib.snprintf(emsg, lt + clen + 128,
|
||||
"无法转换为 MLIR 类型文本(v1 未覆盖该形态):'%s' [行: %s]",
|
||||
emb, _MlirCtxLine)
|
||||
else:
|
||||
viperlib.snprintf(emsg, lt + clen + 128,
|
||||
"无法转换为 MLIR 类型文本(v1 未覆盖该形态):'%s'", emb)
|
||||
MlirFail(emsg)
|
||||
MlirFail("无法转换为 MLIR 类型文本(v1 未覆盖该形态)")
|
||||
|
||||
@@ -653,12 +925,61 @@ def _MlirStructText(dst: str, dstsz: sizet, p: str, lt: sizet,
|
||||
|
||||
def _MlirNamedStructText(dst: str, dstsz: sizet, p: str, lt: sizet,
|
||||
pool: memhub.MemBuddy*, depth: int):
|
||||
"""命名结构体类型文本(形如 %name):v1 不支持 → 响亮报错
|
||||
"""命名结构体类型文本(形如 `%"name"` / `%name`)→ 使用处的内联具名形态
|
||||
|
||||
builders 内嵌类型由 TypePrint 内联渲染为 !llvm.struct<(…)>,
|
||||
中间指令文本不应再出现"只有名字"的结构体类型。
|
||||
MLIR 无独立类型定义语法、不允许无体前向声明(实测 mlir-opt 22.1.8:
|
||||
`!llvm.struct<"x">` 报 "struct without a body only allowed in a recursive
|
||||
struct"),故名字必须回查**模块里已登记的完整定义**(登记表由 __module.vp
|
||||
的 PrintModule 按 NamedTypeHead 发布)才能展开为
|
||||
`!llvm.struct<"name", (字段…)>`;packed 标记与类型同槽登记,一并取出,
|
||||
故 packed 具名结构体在此路径输出 `!llvm.struct<"name", packed (…)>`。
|
||||
文本里的名字可能带 LLVM 口径引号(`%"a.b"`)——剥掉 `%` 与引号后,名字
|
||||
原样作 MLIR 字符串字面量内容(实测 `[`/`]`/数字开头无需额外转义)。
|
||||
查不到 / 仍是 opaque / 名字非法 ⇒ 响亮报错(绝不静默输出空体错误布局)。
|
||||
"""
|
||||
MlirFail("MLIR 渲染: 命名结构体类型文本 v1 不支持(应使用内联 !llvm.struct<(…)>)")
|
||||
if dst is None or dstsz == 0 or p is None:
|
||||
return
|
||||
nm: str = pool.alloc(lt + 2)
|
||||
if nm is None:
|
||||
MlirFail("MLIR 渲染: 命名结构体名字缓冲分配失败")
|
||||
n: sizet = 0
|
||||
q: str = p + 1
|
||||
if q[0] == '"':
|
||||
q = q + 1
|
||||
while q[0] != '\0' and q[0] != '"' and n < lt:
|
||||
nm[n] = q[0]
|
||||
n += 1
|
||||
q = q + 1
|
||||
else:
|
||||
while q[0] != '\0' and _MlirNamedNameEnd(q[0]) == 0 and n < lt:
|
||||
nm[n] = q[0]
|
||||
n += 1
|
||||
q = q + 1
|
||||
nm[n] = '\0'
|
||||
si: int = _MlirNamedFindSlot(nm)
|
||||
if si < 0:
|
||||
_MlirFailNamed(pool,
|
||||
"MLIR 渲染: 命名结构体类型文本未在模块中登记完整定义(MLIR 不允许无体前向声明,须以 !llvm.struct<\"name\", (…)> 内联形态使用)",
|
||||
nm)
|
||||
slot: sizet* = _MlirNamedSlot(si)
|
||||
ty: rawptr[LLVMType] = (rawptr[LLVMType])(slot[0])
|
||||
packed: int = int(slot[1])
|
||||
match ty:
|
||||
case LLVMType.Struct(fields, fcount, _):
|
||||
_MlirNamedStructAppend(dst, dstsz, nm, fields, fcount, packed, pool, depth)
|
||||
case _:
|
||||
_MlirFailNamed(pool,
|
||||
"MLIR 渲染: 登记表中该名字不是结构体类型",
|
||||
nm)
|
||||
|
||||
|
||||
def _MlirNamedNameEnd(ch: int) -> int:
|
||||
"""裸命名结构体名(`%name`)的终止字符(空白 / 顶层分隔符)"""
|
||||
if ch == 0 or ch == 32 or ch == 9 or ch == 10 or ch == 13:
|
||||
return 1
|
||||
if ch == ',' or ch == ')' or ch == ']' or ch == '}' or ch == '(' or ch == '{' or ch == '[':
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _MlirFindFuncParen(p: str, lt: sizet) -> sizet:
|
||||
@@ -766,9 +1087,18 @@ def _MlirTypePrintDepth(buf: str, size: sizet,
|
||||
_strappend(buf, size, tmp)
|
||||
_strappend(buf, size, ">")
|
||||
case LLVMType.Struct(fields, fcount, name):
|
||||
# MLIR 侧不区分匿名/命名:一律渲染字段列表
|
||||
# (指针场景已由 Ptr → !llvm.ptr 收敛,名字不参与渲染)
|
||||
_MlirStructFields(buf, size, fields, pool, depth)
|
||||
# 命名结构体(Name 非 None):内联具名形态 !llvm.struct<"name", (…)>,
|
||||
# 名字参与渲染(MLIR 无定义行,名字只在使用处出现)。
|
||||
# packed 标记不在 LLVMType 上(Struct 变体无该字段),而在 NamedTypeNode
|
||||
# 上 ⇒ 由登记表按名字回查(模块打印期已发布;独立 TypePrint 无表 ⇒ 非 packed)。
|
||||
# 匿名结构体(Name 为 None):沿用 !llvm.struct<(…)>(无关键字位)。
|
||||
# 指针场景已由 Ptr → !llvm.ptr 收敛(不透明指针),不在此分支。
|
||||
if name is not None:
|
||||
buf[0] = '\0'
|
||||
_MlirNamedStructAppend(buf, size, name, fields, fcount,
|
||||
_MlirNamedPackedOf(name), pool, depth)
|
||||
else:
|
||||
_MlirStructFields(buf, size, fields, pool, depth)
|
||||
case LLVMType.Func(ret, params, count):
|
||||
# MLIR 函数类型:!llvm.func<ret (ty1, ty2, ...)>
|
||||
# 与文本→文本的 _MlirFuncText 保持同形外壳。
|
||||
@@ -949,10 +1279,11 @@ def _DeepCopyDepth(pool: memhub.MemBuddy*,
|
||||
"""
|
||||
if ty is None:
|
||||
return None
|
||||
new_ty: rawptr[LLVMType] = (rawptr[LLVMType])(pool.alloc(LLVMType.__sizeof__()))
|
||||
sz_dc: sizet = _TypeSizeGuard()
|
||||
new_ty: rawptr[LLVMType] = (rawptr[LLVMType])(pool.alloc(sz_dc))
|
||||
if new_ty is None:
|
||||
return None
|
||||
string.memset(new_ty, 0, LLVMType.__sizeof__())
|
||||
string.memset(new_ty, 0, sz_dc)
|
||||
if depth >= DEEPCOPY_MAX_DEPTH:
|
||||
# 深度超限:浅拷贝 tag 后停止递归(复制当前变体但不深拷子类型)
|
||||
match ty:
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ def chmod(path: str, mode: int) -> int:
|
||||
# Table of Contents Listing
|
||||
# =============================================================================
|
||||
|
||||
def listdir(path: str, out_names: str, max_count: ULONG) -> ULONG:
|
||||
def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
|
||||
"""列举目录下的文件/子目录名。
|
||||
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
|
||||
文件名通过 _mbuddy 分配内存。
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@ def chmod_impl(path: str, mode: int) -> int:
|
||||
# 目录列举(opendir / readdir / closedir)
|
||||
# =============================================================================
|
||||
|
||||
def listdir(path: str, out_names: str, max_count: ULONG) -> ULONG:
|
||||
def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
|
||||
"""列举目录下的文件/子目录名。
|
||||
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
|
||||
文件名通过 _mbuddy 分配内存。
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ def chmod(path: str, mode: int) -> int:
|
||||
# 目录列举(FindFirstFile / FindNextFile)
|
||||
# =============================================================================
|
||||
|
||||
def listdir(path: str, out_names: str, max_count: ULONG) -> ULONG:
|
||||
def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
|
||||
"""列举目录下的文件/子目录名。
|
||||
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
|
||||
文件名通过 _mbuddy 分配内存。
|
||||
|
||||
+15
-15
@@ -77,17 +77,17 @@ def _run_win32(args: str, capture_output: bool, text: bool) -> CompletedProcess
|
||||
# Security attributes (allow handle inheritance)
|
||||
sa: w32.win32base.SECURITY_ATTRIBUTES = w32.win32base.SECURITY_ATTRIBUTES()
|
||||
sa.nLength = w32.win32base.SECURITY_ATTRIBUTES.__sizeof__()
|
||||
sa.lpSecurityDescriptor = t.CPtr(0)
|
||||
sa.lpSecurityDescriptor = None
|
||||
sa.bInheritHandle = 1 # TRUE
|
||||
|
||||
# Create stdout pipe
|
||||
stdout_read: w32.win32base.HANDLE = t.CPtr(0)
|
||||
stdout_write: w32.win32base.HANDLE = t.CPtr(0)
|
||||
stdout_read: w32.win32base.HANDLE = None
|
||||
stdout_write: w32.win32base.HANDLE = None
|
||||
if capture_output:
|
||||
pipe_result: BOOL = w32.win32file.CreatePipe(
|
||||
t.CPtr(c.Addr(stdout_read)),
|
||||
t.CPtr(c.Addr(stdout_write)),
|
||||
t.CPtr(c.Addr(sa)),
|
||||
&stdout_read,
|
||||
&stdout_write,
|
||||
&sa,
|
||||
0
|
||||
)
|
||||
if pipe_result == 0:
|
||||
@@ -110,16 +110,16 @@ def _run_win32(args: str, capture_output: bool, text: bool) -> CompletedProcess
|
||||
# use 0 when capture_output=False (inherit parent process console, let stdout directly display)
|
||||
create_flags: ULONG = w32.win32process.CREATE_NO_WINDOW if capture_output else 0
|
||||
create_result: BOOL = w32.win32process.CreateProcessA(
|
||||
t.CPtr(0), # lpApplicationName (NULL, parse from command line)
|
||||
None, # lpApplicationName (NULL, parse from command line)
|
||||
cmd_line, # lpCommandLine (modifiable)
|
||||
t.CPtr(0), # lpProcessAttributes
|
||||
t.CPtr(0), # lpThreadAttributes
|
||||
None, # lpProcessAttributes
|
||||
None, # lpThreadAttributes
|
||||
1, # bInheritHandles = TRUE
|
||||
create_flags, # dwCreationFlags
|
||||
t.CPtr(0), # lpEnvironment (inherit from parent process)
|
||||
t.CPtr(0), # lpCurrentDirectory (inherit from parent process)
|
||||
t.CPtr(c.Addr(si)),
|
||||
t.CPtr(c.Addr(pi))
|
||||
None, # lpEnvironment (inherit from parent process)
|
||||
None, # lpCurrentDirectory (inherit from parent process)
|
||||
&si,
|
||||
&pi
|
||||
)
|
||||
|
||||
if create_result == 0:
|
||||
@@ -148,8 +148,8 @@ def _run_win32(args: str, capture_output: bool, text: bool) -> CompletedProcess
|
||||
stdout_read,
|
||||
buf + total,
|
||||
buf_size - 1 - total,
|
||||
t.CPtr(c.Addr(bytes_read)),
|
||||
t.CPtr(0)
|
||||
&bytes_read,
|
||||
None
|
||||
)
|
||||
if read_result == 0 or bytes_read == 0:
|
||||
break
|
||||
|
||||
@@ -59,16 +59,24 @@ def _init_argv():
|
||||
"""Initialize global argv once on first access"""
|
||||
global _argc, _argv
|
||||
if _argc != 0: return
|
||||
_init_argv_win32() if platmacro.IS_WINDOWS else _init_argv_posix()
|
||||
# 必须用 if/else 派发:编译器只翻译"调用表达式语句",三元表达式语句会被丢弃(argv 恒空)
|
||||
if platmacro.IS_WINDOWS:
|
||||
_init_argv_win32()
|
||||
else:
|
||||
_init_argv_posix()
|
||||
|
||||
|
||||
def _init_argv_win32():
|
||||
"""Windows: GetCommandLineA + manual parsing (handle spaces/double quotes/backslashes)"""
|
||||
global _argc, _argv
|
||||
cmdline: str = w32.win32base.GetCommandLineA()
|
||||
if cmdline == None: return
|
||||
if cmdline == None:
|
||||
stdio.printf("ERROR: GetCommandLineA 返回 NULL,无法获取命令行,argv 将为空\n")
|
||||
return
|
||||
cmdlen: sizet = len(cmdline)
|
||||
if cmdlen == 0: return
|
||||
if cmdlen == 0:
|
||||
stdio.printf("ERROR: 命令行字符串为空,无法获取参数,argv 将为空\n")
|
||||
return
|
||||
# Allocate buffer for parameter strings (worst case: entire command line + argc \0s)
|
||||
buf: bytes = _mbuddy.alloc(cmdlen + 1)
|
||||
if buf == None: return
|
||||
|
||||
+1854
File diff suppressed because it is too large.
Load diff
@@ -23,7 +23,7 @@ class Vector[T]():
|
||||
self._grow()
|
||||
if self.length >= self.capacity: return
|
||||
offset: uint64 = uint64(self.data) + self.length * self.elem_size
|
||||
dst: rawptr = rawptr(offset)
|
||||
dst: T* = (T*)offset # typed pointer: store whole T, not its first byte
|
||||
*dst = value
|
||||
self.length += 1
|
||||
|
||||
@@ -47,7 +47,7 @@ class Vector[T]():
|
||||
if index >= self.length: return
|
||||
if self.data == None: return
|
||||
offset: uint64 = uint64(self.data) + index * self.elem_size
|
||||
dst: rawptr = rawptr(offset)
|
||||
dst: T* = (T*)offset # typed pointer: store whole T, not its first byte
|
||||
*dst = value
|
||||
|
||||
def len(self) -> sizet:
|
||||
|
||||
+1396
File diff suppressed because it is too large.
Load diff
+4
-4
@@ -3,7 +3,7 @@ from stdint import *
|
||||
from w32.win32base import *
|
||||
|
||||
# =============================================================================
|
||||
# dbghelp / StackWalk64 声明(供 includes/dbgstack.vp 的 Ctrl+C 栈转储使用)
|
||||
# dbghelp / StackWalk64 声明(供 includes/traceback.vp 的 Ctrl+C 栈转储使用)
|
||||
#
|
||||
# 声明本仓用到的 dbghelp 栈回溯 API 与三个栈帧结构体:
|
||||
# SymInitialize 初始化 dbghelp 的进程上下文(fInvadeProcess=TRUE 只枚举模块、
|
||||
@@ -13,10 +13,10 @@ from w32.win32base import *
|
||||
# SymGetModuleBase64 按 RIP 取模块基址
|
||||
# StackWalk64 逐帧回溯(MachineType=IMAGE_FILE_MACHINE_AMD64)
|
||||
#
|
||||
# 其中 SymInitialize / SymSetOptions / StackWalk64 由 dbgstack.vp **直接调用**(静态引用);
|
||||
# 其中 SymInitialize / SymSetOptions / StackWalk64 由 traceback.vp **直接调用**(静态引用);
|
||||
# SymFunctionTableAccess64 / SymGetModuleBase64 既作为 StackWalk64 的回调原型在此登记,也被
|
||||
# dbgstack.vp **直接调用**做首帧取证诊断(首帧返回码/AddrPC/SymFunctionTableAccess64 返回值,
|
||||
# 用于判定 StackWalk64 "0 帧"的根因)——同样属静态引用。dbgstack.vp 仍用 GetProcAddress 取它们
|
||||
# traceback.vp **直接调用**做首帧取证诊断(首帧返回码/AddrPC/SymFunctionTableAccess64 返回值,
|
||||
# 用于判定 StackWalk64 "0 帧"的根因)——同样属静态引用。traceback.vp 仍用 GetProcAddress 取它们
|
||||
# 的地址以填入 StackWalk64 的回调槽,为的是避开「跨模块函数名 → 函数指针」的隐式转换
|
||||
# (该动态解析写法见 VET/App/lib/VGuard.vp:852-867)。
|
||||
#
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ extern def LocalFree(hMem: VOIDPTR) -> VOIDPTR
|
||||
# BOOL ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer,
|
||||
# SIZE_T nSize, SIZE_T *lpNumberOfBytesRead);
|
||||
# 该导出的签名恰为 dbghelp 的 PREAD_PROCESS_MEMORY_ROUTINE64:StackWalk64 的回调槽
|
||||
# 要求填这个函数(includes/dbgstack.vp 在 Install 时用 GetProcAddress 取其地址传入,
|
||||
# 要求填这个函数(includes/traceback.vp 在 Install 时用 GetProcAddress 取其地址传入,
|
||||
# 见 includes/w32/win32dbghelp.vp 的 StackWalk64)。
|
||||
# 返回 0 = 失败(GetLastError 取原因)。
|
||||
extern def ReadProcessMemory(hProcess: HANDLE, lpBaseAddress: t.CConst | VOIDPTR, lpBuffer: VOIDPTR, nSize: sizet, lpNumberOfBytesRead: sizet | t.CPtr) -> BOOL
|
||||
Reference in new issue
Block a user