397 lines
14 KiB
Python
397 lines
14 KiB
Python
import t, c
|
||
from stdint import *
|
||
import string
|
||
import viperlib
|
||
import memhub
|
||
from linkedlist import GSListNode, GSList
|
||
from .__function import Function, BasicBlock, Line, Param
|
||
from .__module import LLVMModule
|
||
|
||
|
||
# ============================================================
|
||
# SSA 验证器
|
||
#
|
||
# 检查项:
|
||
# 1. 块终止: 每个基本块必须以终止指令结尾 (ret/br/switch/unreachable)
|
||
# 2. SSA 唯一性: 每个 %name 只能被定义一次
|
||
# 3. 未定义引用: 所有 %name 使用必须有对应定义
|
||
# 4. Phi 位置: phi 指令只能出现在块首(非 phi 指令之前)
|
||
# ============================================================
|
||
|
||
# 错误码
|
||
VERIFY_OK: t.CDefine = 0
|
||
VERIFY_ERR_NO_TERMINATOR: t.CDefine = 1
|
||
VERIFY_ERR_DUPLICATE_DEF: t.CDefine = 2
|
||
VERIFY_ERR_UNDEFINED_USE: t.CDefine = 3
|
||
VERIFY_ERR_PHI_NOT_FIRST: t.CDefine = 4
|
||
|
||
|
||
# ============================================================
|
||
# NameEntry: 已定义 SSA 名条目(链表节点)
|
||
# ============================================================
|
||
class NameEntry(GSListNode[NameEntry]):
|
||
Name: t.CChar | t.CPtr # SSA 名(如 "%0"/"%result")
|
||
|
||
|
||
# ============================================================
|
||
# VerifyResult: 验证结果
|
||
# ============================================================
|
||
class VerifyResult:
|
||
ErrorCount: t.CInt # 错误总数
|
||
WarningCount: t.CInt # 警告总数
|
||
ErrorCode: t.CInt # 第一个错误码
|
||
ErrorBuf: t.CChar | t.CPtr # 错误消息缓冲区
|
||
ErrorBufSize: t.CSizeT # 缓冲区大小
|
||
|
||
|
||
# ============================================================
|
||
# 辅助函数
|
||
# ============================================================
|
||
def _new_verify_result(pool: memhub.MemBuddy | t.CPtr,
|
||
buf_size: t.CSizeT) -> VerifyResult | t.CPtr:
|
||
"""创建验证结果对象"""
|
||
ptr: VerifyResult | t.CPtr = pool.alloc(VerifyResult.__sizeof__())
|
||
if ptr is None: return None
|
||
string.memset(ptr, 0, VerifyResult.__sizeof__())
|
||
ptr.ErrorCount = 0
|
||
ptr.WarningCount = 0
|
||
ptr.ErrorCode = VERIFY_OK
|
||
buf: t.CChar | t.CPtr = pool.alloc(buf_size)
|
||
if buf is not None:
|
||
buf[0] = '\0'
|
||
ptr.ErrorBuf = buf
|
||
ptr.ErrorBufSize = buf_size
|
||
return ptr
|
||
|
||
|
||
def _report_error(result: VerifyResult | t.CPtr, code: t.CInt, msg: t.CChar | t.CPtr):
|
||
"""记录一个错误"""
|
||
if result is None: return
|
||
result.ErrorCount += 1
|
||
if result.ErrorCode == VERIFY_OK:
|
||
result.ErrorCode = code
|
||
if result.ErrorBuf is not None and msg is not None:
|
||
_append_cstr(result.ErrorBuf, result.ErrorBufSize, msg)
|
||
_append_cstr(result.ErrorBuf, result.ErrorBufSize, "\n")
|
||
|
||
|
||
def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr):
|
||
"""将 src 追加到 dst 末尾"""
|
||
if dst is None or src is None: return
|
||
dlen: t.CSizeT = string.strlen(dst)
|
||
slen: t.CSizeT = string.strlen(src)
|
||
remain: t.CSizeT = dst_size - dlen
|
||
if remain <= 0: return
|
||
i: t.CSizeT = 0
|
||
while i < slen and i + 1 < remain:
|
||
dst[dlen + i] = src[i]
|
||
i += 1
|
||
dst[dlen + i] = '\0'
|
||
|
||
|
||
# ============================================================
|
||
# SSA 名表操作
|
||
# ============================================================
|
||
def _new_name_table(pool: memhub.MemBuddy | t.CPtr) -> GSList[NameEntry] | t.CPtr:
|
||
"""创建空名表"""
|
||
gsl: GSList[NameEntry] | t.CPtr = pool.alloc(24) # GSList: Head+Tail+Count
|
||
if gsl is None: return None
|
||
string.memset(gsl, 0, 24)
|
||
return gsl
|
||
|
||
|
||
# NameEntry 硬编码大小: Next(8) + Name(8) = 16 字节
|
||
NENTRY_SIZE: t.CDefine = 16
|
||
|
||
def _name_table_add(table: GSList[NameEntry] | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
name: t.CChar | t.CPtr) -> t.CInt:
|
||
"""添加名到表。返回 1=重复, 0=成功添加"""
|
||
if table is None or name is None: return 0
|
||
# 检查重复
|
||
cur: NameEntry | t.CPtr = table.Head
|
||
while cur is not None:
|
||
if string.strcmp(cur.Name, name) == 0:
|
||
return 1
|
||
cur = cur.Next
|
||
# 添加新条目
|
||
entry: NameEntry | t.CPtr = pool.alloc(NENTRY_SIZE)
|
||
if entry is None: return 0
|
||
string.memset(entry, 0, NENTRY_SIZE)
|
||
entry.Name = name
|
||
table.append(entry)
|
||
return 0
|
||
|
||
|
||
def _name_table_contains(table: GSList[NameEntry] | t.CPtr,
|
||
name: t.CChar | t.CPtr) -> t.CInt:
|
||
"""检查名是否在表中。返回 1=存在, 0=不存在"""
|
||
if table is None or name is None: return 0
|
||
cur: NameEntry | t.CPtr = table.Head
|
||
while cur is not None:
|
||
if string.strcmp(cur.Name, name) == 0:
|
||
return 1
|
||
cur = cur.Next
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# 文本分析辅助
|
||
# ============================================================
|
||
def _is_char_name(ch: t.CChar) -> t.CInt:
|
||
"""判断字符是否为 SSA 名字符 [a-zA-Z0-9._]"""
|
||
if ch >= 'a' and ch <= 'z': return 1
|
||
if ch >= 'A' and ch <= 'Z': return 1
|
||
if ch >= '0' and ch <= '9': return 1
|
||
if ch == '.' or ch == '_': return 1
|
||
return 0
|
||
|
||
|
||
def _extract_def_name(pool: memhub.MemBuddy | t.CPtr,
|
||
text: t.CChar | t.CPtr) -> t.CChar | t.CPtr:
|
||
"""从定义行提取被定义的 SSA 名。
|
||
|
||
定义行格式: "%<name> = <instruction>"
|
||
返回分配的 NUL 结尾名串(如 "%0"),或 None 若非定义行。
|
||
"""
|
||
if text is None: return None
|
||
if text[0] != '%': return None
|
||
# 扫描 " = " 模式
|
||
i: t.CInt = 1
|
||
while text[i] != '\0':
|
||
if text[i] == ' ' and text[i + 1] == '=' and text[i + 2] == ' ':
|
||
# 找到 " = " 在位置 i
|
||
name_len: t.CInt = i
|
||
name: t.CChar | t.CPtr = pool.alloc(name_len + 1)
|
||
if name is None: return None
|
||
j: t.CInt = 0
|
||
while j < name_len:
|
||
name[j] = text[j]
|
||
j += 1
|
||
name[name_len] = '\0'
|
||
return name
|
||
i += 1
|
||
return None
|
||
|
||
|
||
def _is_terminator(text: t.CChar | t.CPtr) -> t.CInt:
|
||
"""判断文本是否为终止指令"""
|
||
if text is None: return 0
|
||
if string.strncmp(text, "ret", 3) == 0: return 1
|
||
if string.strncmp(text, "br ", 3) == 0: return 1
|
||
if string.strncmp(text, "switch ", 7) == 0: return 1
|
||
if string.strncmp(text, "unreachable", 11) == 0: return 1
|
||
if string.strncmp(text, "indirectbr ", 11) == 0: return 1
|
||
if string.strncmp(text, "resume ", 7) == 0: return 1
|
||
return 0
|
||
|
||
|
||
def _is_phi(text: t.CChar | t.CPtr) -> t.CInt:
|
||
"""判断文本是否为 phi 指令"""
|
||
if text is None: return 0
|
||
if string.strstr(text, " = phi ") is not None: return 1
|
||
return 0
|
||
|
||
|
||
def _check_uses_in_line(table: GSList[NameEntry] | t.CPtr,
|
||
text: t.CChar | t.CPtr,
|
||
def_name: t.CChar | t.CPtr) -> t.CInt:
|
||
"""检查行中所有 SSA 名使用是否已定义。
|
||
|
||
跳过 def_name(定义位置的名字)。
|
||
返回未定义使用数量。
|
||
"""
|
||
if text is None: return 0
|
||
i: t.CInt = 0
|
||
undef_count: t.CInt = 0
|
||
while text[i] != '\0':
|
||
if text[i] == '%':
|
||
# 提取名
|
||
name_start: t.CInt = i
|
||
i += 1
|
||
while text[i] != '\0' and _is_char_name(text[i]) == 1:
|
||
i += 1
|
||
name_len: t.CInt = i - name_start
|
||
if name_len > 1:
|
||
# 构建临时名串进行比较
|
||
# 由于无法在栈上分配,用 strstr 进行简化检查
|
||
# 这里采用逐字符比较方式
|
||
if _name_matches_at(text, name_start, name_len, table) == 0:
|
||
# 检查是否是 def_name(定义本身的位置)
|
||
if def_name is not None:
|
||
if _str_matches_at(text, name_start, name_len, def_name) == 0:
|
||
undef_count += 1
|
||
else:
|
||
undef_count += 1
|
||
else:
|
||
i += 1
|
||
return undef_count
|
||
|
||
|
||
def _name_matches_at(text: t.CChar | t.CPtr, start: t.CInt, length: t.CInt,
|
||
table: GSList[NameEntry] | t.CPtr) -> t.CInt:
|
||
"""检查 text[start:start+length] 是否匹配名表中的某个条目。
|
||
返回 1=匹配, 0=不匹配
|
||
"""
|
||
if table is None: return 0
|
||
cur: NameEntry | t.CPtr = table.Head
|
||
while cur is not None:
|
||
if cur.Name is not None:
|
||
if _str_matches_at(text, start, length, cur.Name) == 1:
|
||
return 1
|
||
cur = cur.Next
|
||
return 0
|
||
|
||
|
||
def _str_matches_at(text: t.CChar | t.CPtr, start: t.CInt, length: t.CInt,
|
||
target: t.CChar | t.CPtr) -> t.CInt:
|
||
"""检查 text[start:start+length] 是否等于 target 字符串。
|
||
返回 1=匹配, 0=不匹配
|
||
"""
|
||
if text is None or target is None: return 0
|
||
# 检查 target 长度
|
||
target_len: t.CInt = string.strlen(target)
|
||
if target_len != length: return 0
|
||
i: t.CInt = 0
|
||
while i < length:
|
||
if text[start + i] != target[i]: return 0
|
||
i += 1
|
||
return 1
|
||
|
||
|
||
# ============================================================
|
||
# 主验证函数
|
||
# ============================================================
|
||
def verify_function(pool: memhub.MemBuddy | t.CPtr,
|
||
func: Function | t.CPtr,
|
||
result: VerifyResult | t.CPtr) -> t.CInt:
|
||
"""验证函数的 SSA 正确性。
|
||
|
||
参数:
|
||
pool: 内存池
|
||
func: 要验证的函数
|
||
result: 验证结果(调用前需用 _new_verify_result 创建)
|
||
|
||
返回: 错误数量(0 = 验证通过)
|
||
"""
|
||
if func is None or result is None: return 1
|
||
|
||
# 创建名表
|
||
name_table: GSList[NameEntry] | t.CPtr = _new_name_table(pool)
|
||
if name_table is None: return 1
|
||
|
||
# 1. 将函数参数加入名表(参数是已定义的)
|
||
param: Param | t.CPtr = None
|
||
if func.Params is not None:
|
||
param = func.Params.Head
|
||
while param is not None:
|
||
if param.Name is not None:
|
||
_name_table_add(name_table, pool, param.Name)
|
||
param = param.Next
|
||
|
||
# 1b. 将基本块名加入名表(块标签如 %then 是 br/phi 的合法引用目标)
|
||
blk_iter: BasicBlock | t.CPtr = None
|
||
if func.Blocks is not None:
|
||
blk_iter = func.Blocks.Head
|
||
while blk_iter is not None:
|
||
if blk_iter.Name is not None:
|
||
blk_name_len: t.CSizeT = string.strlen(blk_iter.Name)
|
||
blk_ref: t.CChar | t.CPtr = pool.alloc(blk_name_len + 2)
|
||
if blk_ref is not None:
|
||
# 构造 "%<name>" 字符串
|
||
blk_ref[0] = '%'
|
||
k: t.CSizeT = 0
|
||
while k < blk_name_len:
|
||
blk_ref[k + 1] = blk_iter.Name[k]
|
||
k += 1
|
||
blk_ref[blk_name_len + 1] = '\0'
|
||
_name_table_add(name_table, pool, blk_ref)
|
||
blk_iter = blk_iter.Next
|
||
|
||
# 2. 遍历所有块
|
||
blk: BasicBlock | t.CPtr = None
|
||
if func.Blocks is not None:
|
||
blk = func.Blocks.Head
|
||
while blk is not None:
|
||
# 检查终止指令
|
||
if blk.IsTerminated == 0:
|
||
# 进一步检查最后一行是否为终止指令
|
||
has_term: t.CInt = 0
|
||
line_check: Line | t.CPtr = None
|
||
if blk.Lines is not None:
|
||
line_check = blk.Lines.Head
|
||
while line_check is not None:
|
||
if line_check.Buf is not None:
|
||
if _is_terminator(line_check.Buf) == 1:
|
||
has_term = 1
|
||
line_check = line_check.Next
|
||
if has_term == 0:
|
||
blk_name: t.CChar | t.CPtr = "<unnamed>"
|
||
if blk.Name is not None:
|
||
blk_name = blk.Name
|
||
err_buf: t.CChar | t.CPtr = pool.alloc(128)
|
||
if err_buf is not None:
|
||
viperlib.snprintf(err_buf, 128, "ERROR: block '%s' missing terminator", blk_name)
|
||
_report_error(result, VERIFY_ERR_NO_TERMINATOR, err_buf)
|
||
|
||
# 遍历块内指令行
|
||
seen_non_phi: t.CInt = 0
|
||
line: Line | t.CPtr = None
|
||
if blk.Lines is not None:
|
||
line = blk.Lines.Head
|
||
while line is not None:
|
||
if line.Buf is not None:
|
||
text: t.CChar | t.CPtr = line.Buf
|
||
|
||
# 检查 phi 位置
|
||
if _is_phi(text) == 1:
|
||
if seen_non_phi == 1:
|
||
phi_err: t.CChar | t.CPtr = pool.alloc(128)
|
||
if phi_err is not None:
|
||
viperlib.snprintf(phi_err, 128, "ERROR: phi not at block start")
|
||
_report_error(result, VERIFY_ERR_PHI_NOT_FIRST, phi_err)
|
||
else:
|
||
# 非空行且非 phi 标记为已见非 phi
|
||
if string.strlen(text) > 0:
|
||
seen_non_phi = 1
|
||
|
||
# 提取定义名
|
||
def_name: t.CChar | t.CPtr = _extract_def_name(pool, text)
|
||
if def_name is not None:
|
||
# 检查重复定义
|
||
dup: t.CInt = _name_table_add(name_table, pool, def_name)
|
||
if dup == 1:
|
||
dup_err: t.CChar | t.CPtr = pool.alloc(128)
|
||
if dup_err is not None:
|
||
viperlib.snprintf(dup_err, 128, "ERROR: duplicate definition '%s'", def_name)
|
||
_report_error(result, VERIFY_ERR_DUPLICATE_DEF, dup_err)
|
||
|
||
# 检查未定义使用
|
||
undef: t.CInt = _check_uses_in_line(name_table, text, def_name)
|
||
if undef > 0:
|
||
use_err: t.CChar | t.CPtr = pool.alloc(128)
|
||
if use_err is not None:
|
||
viperlib.snprintf(use_err, 128, "ERROR: %d undefined use(s) in: %s", undef, text)
|
||
_report_error(result, VERIFY_ERR_UNDEFINED_USE, use_err)
|
||
|
||
line = line.Next
|
||
|
||
blk = blk.Next
|
||
|
||
return result.ErrorCount
|
||
|
||
|
||
def verify_module(pool: memhub.MemBuddy | t.CPtr, mod: LLVMModule | t.CPtr, result: VerifyResult | t.CPtr) -> t.CInt:
|
||
"""验证模块中所有函数的 SSA 正确性。
|
||
|
||
返回: 错误总数
|
||
"""
|
||
if mod is None or result is None: return 1
|
||
func: Function | t.CPtr = mod.FuncHead
|
||
while func is not None:
|
||
if func.IsDeclared == 0:
|
||
# 只验证有函数体的定义,跳过声明
|
||
verify_function(pool, func, result)
|
||
func = func.Next
|
||
return result.ErrorCount
|