修正了一些错误

This commit is contained in:
2026-07-26 20:32:26 +08:00
parent ca7c2120b8
commit 1837339f69
203 changed files with 374300 additions and 2638 deletions

View File

@@ -126,19 +126,34 @@ class ArgumentParser:
def __new__(self, prog: str = "", description: str = "",
pool: memhub.MemBuddy | t.CPtr = None) -> t.CPtr:
stdio.printf("[DBG-argparse] __new__ enter pool=%p\n", pool)
stdio.fflush(0)
mb: memhub.MemBuddy | t.CPtr = pool
if mb is None: mb = _mbuddy
return mb.alloc(40)
stdio.printf("[DBG-argparse] __new__ mb=%p\n", mb)
stdio.fflush(0)
ret: t.CPtr = mb.alloc(40)
stdio.printf("[DBG-argparse] __new__ ret=%p\n", ret)
stdio.fflush(0)
return ret
def __init__(self, prog: str = "", description: str = "",
pool: memhub.MemBuddy | t.CPtr = None):
stdio.printf("[DBG-argparse] __init__ enter self=%p pool=%p\n", self, pool)
stdio.fflush(0)
mb: memhub.MemBuddy | t.CPtr = pool
if mb is None: mb = _mbuddy
stdio.printf("[DBG-argparse] __init__ mb=%p\n", mb)
stdio.fflush(0)
self.__mbuddy__ = mb
self._prog = prog
self._description = description
self._arg_count = 0
stdio.printf("[DBG-argparse] __init__ before alloc\n")
stdio.fflush(0)
self._args = mb.alloc(MAX_ARGS * Argument.__sizeof__())
stdio.printf("[DBG-argparse] __init__ after alloc _args=%p\n", self._args)
stdio.fflush(0)
def add_argument(self, name: str = "", short: str = None,
arg_type: INT = 0, default: INT = 0,
@@ -156,10 +171,18 @@ class ArgumentParser:
action: 动作STORE/STORE_TRUE/STORE_FALSE/COUNT
help: 帮助文本
"""
stdio.printf("[DBG-argparse] enter add_argument name=%s args_ptr=%p\n", name, self._args)
stdio.fflush(0)
if self._arg_count >= MAX_ARGS: return
idx: INT = self._arg_count
stdio.printf("[DBG-argparse] idx=%d sizeof(Argument)=%d\n", idx, Argument.__sizeof__())
stdio.fflush(0)
arg: Argument | t.CPtr = t.CPtr(t.CUInt64T(self._args) + idx * Argument.__sizeof__())
stdio.printf("[DBG-argparse] arg ptr=%p\n", arg)
stdio.fflush(0)
arg.name = name
stdio.printf("[DBG-argparse] after arg.name=%s\n", arg.name)
stdio.fflush(0)
arg.short_name = short
arg.help_text = help
arg.arg_type = arg_type
@@ -167,6 +190,8 @@ class ArgumentParser:
arg.default_int = default
arg.default_str = default_str
arg.required = required
stdio.printf("[DBG-argparse] before dest logic\n")
stdio.fflush(0)
# 判断是否位置参数
if name is not None and name[0] != '-':
arg.is_positional = True
@@ -177,7 +202,11 @@ class ArgumentParser:
arg.dest = name + 2
else:
arg.dest = name + 1
stdio.printf("[DBG-argparse] after dest logic\n")
stdio.fflush(0)
self._arg_count = idx + 1
stdio.printf("[DBG-argparse] exit add_argument\n")
stdio.fflush(0)
def _get_arg(self, idx: INT) -> Argument | t.CPtr:
return t.CPtr(t.CUInt64T(self._args) + idx * Argument.__sizeof__())

View File

@@ -439,6 +439,7 @@ def _parse_bitor(ps: Parser | t.CPtr) -> AST | t.CPtr:
if left is None: return None
while _match_op(ps, TokOp.VBar):
_advance(ps)
_skip_nl(ps)
right: AST | t.CPtr = _parse_bitxor(ps)
node: AST | t.CPtr = BinOp(ps.pool, left, OpKind.BitOr, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
@@ -451,6 +452,7 @@ def _parse_bitxor(ps: Parser | t.CPtr) -> AST | t.CPtr:
if left is None: return None
while _match_op(ps, TokOp.Caret):
_advance(ps)
_skip_nl(ps)
right: AST | t.CPtr = _parse_bitand(ps)
node: AST | t.CPtr = BinOp(ps.pool, left, OpKind.BitXor, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
@@ -463,6 +465,7 @@ def _parse_bitand(ps: Parser | t.CPtr) -> AST | t.CPtr:
if left is None: return None
while _match_op(ps, TokOp.Amp):
_advance(ps)
_skip_nl(ps)
right: AST | t.CPtr = _parse_shift(ps)
node: AST | t.CPtr = BinOp(ps.pool, left, OpKind.BitAnd, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
@@ -476,6 +479,7 @@ def _parse_shift(ps: Parser | t.CPtr) -> AST | t.CPtr:
while _match_op(ps, TokOp.LtLt) or _match_op(ps, TokOp.GtGt):
op_token: t.CInt = _cur_op(ps)
_advance(ps)
_skip_nl(ps)
right: AST | t.CPtr = _parse_arith(ps)
ast_op: t.CInt = OpKind.LShift
if op_token == TokOp.GtGt:
@@ -492,6 +496,7 @@ def _parse_arith(ps: Parser | t.CPtr) -> AST | t.CPtr:
while _match_op(ps, TokOp.Plus) or _match_op(ps, TokOp.Minus):
op_token: t.CInt = _cur_op(ps)
_advance(ps)
_skip_nl(ps)
right: AST | t.CPtr = _parse_term(ps)
ast_op: t.CInt = OpKind.Add
if op_token == TokOp.Minus:
@@ -514,6 +519,7 @@ def _parse_term(ps: Parser | t.CPtr) -> AST | t.CPtr:
elif _match_op(ps, TokOp.At): op_token = TokOp.At
if op_token == 0: break
_advance(ps)
_skip_nl(ps)
right: AST | t.CPtr = _parse_factor(ps)
ast_op: t.CInt = _binop_from_op(op_token)
node: AST | t.CPtr = BinOp(ps.pool, left, ast_op, right)
@@ -1624,6 +1630,7 @@ def _parse_alias_list(ps: Parser | t.CPtr) -> list[AST | t.CPtr] | t.CPtr:
names.append(a)
if not _accept_op(ps, TokOp.Comma):
break
_skip_nl(ps)
return names

View File

@@ -3,6 +3,7 @@ from stdint import *
import string
import viperlib
import memhub
import stdio
from linkedlist import GSListNode, GSList
from .__types import LLVMType, TypePrint
from .__values import Value, new_value, SSAValue
@@ -145,6 +146,36 @@ def build_alloca(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> Value |
return SSAValue(pool, result_ty_ptr, name)
def build_alloca_at_entry(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> Value | t.CPtr:
"""%N = alloca <ty>(在函数入口块生成,确保支配性)
在入口块的终止指令之前插入 alloca避免在条件分支内生成 alloca
导致的支配性违规Instruction does not dominate all uses
"""
if builder is None: return None
pool: memhub.MemBuddy | t.CPtr = builder.Pool
func: Function | t.CPtr = builder.Func
if func is None or func.Blocks is None:
return build_alloca(builder, ty)
entry_blk: BasicBlock | t.CPtr = func.Blocks.Head
if entry_blk is None:
return build_alloca(builder, ty)
name: t.CChar | t.CPtr = _alloc_ssa_name(builder)
ty_s: t.CChar | t.CPtr = _type_str(builder, ty)
line_text: t.CChar | t.CPtr = pool.alloc(128)
if line_text is None: return None
viperlib.snprintf(line_text, 128, "%s = alloca %s", name, ty_s)
# 在入口块终止指令之前插入
block_insert_text_before_terminator(pool, entry_blk, line_text)
# 结果类型是 ty*
result_ty: LLVMType | t.CPtr = LLVMType.Ptr(ty)
result_ty_ptr: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if result_ty_ptr is not None:
string.memset(result_ty_ptr, 0, LLVMType.__sizeof__())
c.DerefAs(result_ty_ptr, result_ty)
return SSAValue(pool, result_ty_ptr, name)
def build_load(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr,
ptr: Value | t.CPtr) -> Value | t.CPtr:
"""%N = load <ty>, <ptr_ty> <ptr>"""
@@ -510,30 +541,30 @@ def build_call(builder: IRBuilder | t.CPtr, callee: t.CChar | t.CPtr,
ret_ty_s: t.CChar | t.CPtr = _type_str(builder, ret_ty)
# 构建参数列表文本 + 变参函数类型签名
args_buf: t.CChar | t.CPtr = pool.alloc(256)
args_buf: t.CChar | t.CPtr = pool.alloc(1024)
if args_buf is None: return None
args_buf[0] = '\0'
func_ty_buf: t.CChar | t.CPtr = pool.alloc(256)
func_ty_buf: t.CChar | t.CPtr = pool.alloc(1024)
if func_ty_buf is None: return None
func_ty_buf[0] = '\0'
cur: Value | t.CPtr = args
first: t.CInt = 1
while cur is not None:
if first == 0:
_append_cstr(args_buf, 256, ", ")
_append_cstr(func_ty_buf, 256, ", ")
_append_cstr(args_buf, 1024, ", ")
_append_cstr(func_ty_buf, 1024, ", ")
arg_ty_s: t.CChar | t.CPtr = _type_str(builder, cur.Ty)
_append_cstr(args_buf, 256, arg_ty_s)
_append_cstr(args_buf, 256, " ")
_append_cstr(args_buf, 1024, arg_ty_s)
_append_cstr(args_buf, 1024, " ")
if cur.Name is not None:
_append_cstr(args_buf, 256, cur.Name)
_append_cstr(func_ty_buf, 256, arg_ty_s)
_append_cstr(args_buf, 1024, cur.Name)
_append_cstr(func_ty_buf, 1024, arg_ty_s)
cur = cur.Next
first = 0
if is_variadic == 1:
if first == 0:
_append_cstr(func_ty_buf, 256, ", ")
_append_cstr(func_ty_buf, 256, "...")
_append_cstr(func_ty_buf, 1024, ", ")
_append_cstr(func_ty_buf, 1024, "...")
# 构造带引号的 callee 名字SHA1 前缀名含 '.' 或以数字开头需要加引号)
callee_buf: t.CChar | t.CPtr = pool.alloc(128)
@@ -546,18 +577,18 @@ def build_call(builder: IRBuilder | t.CPtr, callee: t.CChar | t.CPtr,
else:
_append_cstr(callee_buf, 128, callee)
line: t.CChar | t.CPtr = pool.alloc(512)
line: t.CChar | t.CPtr = pool.alloc(2048)
if line is None: return None
if is_variadic == 1:
if is_void == 1:
viperlib.snprintf(line, 512, "call %s (%s) @%s(%s)", ret_ty_s, func_ty_buf, callee_buf, args_buf)
viperlib.snprintf(line, 2048, "call %s (%s) @%s(%s)", ret_ty_s, func_ty_buf, callee_buf, args_buf)
else:
viperlib.snprintf(line, 512, "%s = call %s (%s) @%s(%s)", name, ret_ty_s, func_ty_buf, callee_buf, args_buf)
viperlib.snprintf(line, 2048, "%s = call %s (%s) @%s(%s)", name, ret_ty_s, func_ty_buf, callee_buf, args_buf)
else:
if is_void == 1:
viperlib.snprintf(line, 512, "call %s @%s(%s)", ret_ty_s, callee_buf, args_buf)
viperlib.snprintf(line, 2048, "call %s @%s(%s)", ret_ty_s, callee_buf, args_buf)
else:
viperlib.snprintf(line, 512, "%s = call %s @%s(%s)", name, ret_ty_s, callee_buf, args_buf)
viperlib.snprintf(line, 2048, "%s = call %s @%s(%s)", name, ret_ty_s, callee_buf, args_buf)
_emit(builder, line)
if is_void == 1:
@@ -594,28 +625,28 @@ def build_call_indirect(builder: IRBuilder | t.CPtr, callee_val: Value | t.CPtr,
ret_ty_s: t.CChar | t.CPtr = _type_str(builder, ret_ty)
# 构建参数列表文本
args_buf: t.CChar | t.CPtr = pool.alloc(256)
args_buf: t.CChar | t.CPtr = pool.alloc(1024)
if args_buf is None: return None
args_buf[0] = '\0'
cur: Value | t.CPtr = args
first: t.CInt = 1
while cur is not None:
if first == 0:
_append_cstr(args_buf, 256, ", ")
_append_cstr(args_buf, 1024, ", ")
arg_ty_s: t.CChar | t.CPtr = _type_str(builder, cur.Ty)
_append_cstr(args_buf, 256, arg_ty_s)
_append_cstr(args_buf, 256, " ")
_append_cstr(args_buf, 1024, arg_ty_s)
_append_cstr(args_buf, 1024, " ")
if cur.Name is not None:
_append_cstr(args_buf, 256, cur.Name)
_append_cstr(args_buf, 1024, cur.Name)
cur = cur.Next
first = 0
line: t.CChar | t.CPtr = pool.alloc(512)
line: t.CChar | t.CPtr = pool.alloc(2048)
if line is None: return None
if is_void == 1:
viperlib.snprintf(line, 512, "call %s %s(%s)", ret_ty_s, callee_val.Name, args_buf)
viperlib.snprintf(line, 2048, "call %s %s(%s)", ret_ty_s, callee_val.Name, args_buf)
else:
viperlib.snprintf(line, 512, "%s = call %s %s(%s)", name, ret_ty_s, callee_val.Name, args_buf)
viperlib.snprintf(line, 2048, "%s = call %s %s(%s)", name, ret_ty_s, callee_val.Name, args_buf)
_emit(builder, line)
if is_void == 1:
@@ -885,17 +916,27 @@ def build_gep_struct(builder: IRBuilder | t.CPtr, struct_ty: LLVMType | t.CPtr,
struct_ty_s: t.CChar | t.CPtr = _type_str(builder, struct_ty)
ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty)
line: t.CChar | t.CPtr = pool.alloc(2048)
if line is None: return None
if line is None:
stdio.printf("[BGS] line alloc None\n")
stdio.fflush(0)
return None
viperlib.snprintf(line, 2048, "%s = getelementptr %s, %s %s, i32 0, i32 %d",
name, struct_ty_s, ptr_ty_s, ptr.Name, field_idx)
_emit(builder, line)
# GEP 结果类型是 field_ty*
result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__())
if result_ty is not None:
if result_ty is None:
stdio.printf("[BGS] result_ty alloc None\n")
stdio.fflush(0)
else:
string.memset(result_ty, 0, LLVMType.__sizeof__())
c.DerefAs(result_ty, LLVMType.Ptr(field_ty))
return SSAValue(pool, result_ty, name)
rv: Value | t.CPtr = SSAValue(pool, result_ty, name)
if rv is None:
stdio.printf("[BGS] SSAValue None\n")
stdio.fflush(0)
return rv
# ============================================================
@@ -938,34 +979,35 @@ def build_phi(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr,
ty_s: t.CChar | t.CPtr = _type_str(builder, ty)
# 构建入边列表文本: [val, %blk], [val, %blk], ...
parts: t.CChar | t.CPtr = pool.alloc(512)
# 入边可能很多(如 30+ 个 or 链的 bool.merge需要大缓冲区
parts: t.CChar | t.CPtr = pool.alloc(4096)
if parts is None: return None
parts[0] = '\0'
cur: PhiIncoming | t.CPtr = incoming_head
first: t.CInt = 1
while cur is not None:
if first == 0:
_append_cstr(parts, 512, ", ")
_append_cstr(parts, 512, "[")
_append_cstr(parts, 4096, ", ")
_append_cstr(parts, 4096, "[")
# 使用中间变量避免链式结构体成员访问被编译器丢弃
inc_val: Value | t.CPtr = cur.Val
if inc_val is not None:
inc_val_name: t.CChar | t.CPtr = inc_val.Name
if inc_val_name is not None:
_append_cstr(parts, 512, inc_val_name)
_append_cstr(parts, 512, ", %")
_append_cstr(parts, 4096, inc_val_name)
_append_cstr(parts, 4096, ", %")
inc_blk: BasicBlock | t.CPtr = cur.Block
if inc_blk is not None:
inc_blk_name: t.CChar | t.CPtr = inc_blk.Name
if inc_blk_name is not None:
_append_cstr(parts, 512, inc_blk_name)
_append_cstr(parts, 512, "]")
_append_cstr(parts, 4096, inc_blk_name)
_append_cstr(parts, 4096, "]")
cur = cur.Next
first = 0
line: t.CChar | t.CPtr = pool.alloc(640)
line: t.CChar | t.CPtr = pool.alloc(8192)
if line is None: return None
viperlib.snprintf(line, 640, "%s = phi %s %s", name, ty_s, parts)
viperlib.snprintf(line, 8192, "%s = phi %s %s", name, ty_s, parts)
_emit(builder, line)
return SSAValue(pool, ty, name)

View File

@@ -227,6 +227,48 @@ def block_append_text(pool: memhub.MemBuddy | t.CPtr, block: BasicBlock | t.CPtr
block_append_line(block, line)
def block_insert_text_before_terminator(pool: memhub.MemBuddy | t.CPtr,
block: BasicBlock | t.CPtr,
text: t.CChar | t.CPtr):
"""创建指令行并插入到基本块终止指令之前
如果块未终止,追加到末尾。
如果块已终止(有 br/ret在终止指令之前插入确保 alloca 等指令
不出现在终止指令之后。
"""
if block is None or text is None: return
if block.Lines is None: return
line: Line | t.CPtr = new_line(pool, text)
if line is None: return
# 块未终止:直接追加
if block.IsTerminated == 0:
block_append_line(block, line)
return
# 块已终止:在 Tail终止指令之前插入
lines: GSList[Line] | t.CPtr = block.Lines
if lines.Head is None:
block_append_line(block, line)
return
# 只有一个节点:在 Head 之前插入
if lines.Head is lines.Tail:
line.Next = lines.Head
lines.Head = line
lines.Count += 1
return
# 找到 Tail 的前驱节点
prev: Line | t.CPtr = lines.Head
while prev is not None and prev.Next is not lines.Tail:
prev = prev.Next
if prev is None:
# 找不到前驱,回退到追加
block_append_line(block, line)
return
# 在 prev 和 Tail 之间插入 line
line.Next = lines.Tail
prev.Next = line
lines.Count += 1
# ============================================================
# 属性设置
# ============================================================
@@ -276,6 +318,259 @@ def _ll_name_needs_quote(name: str) -> t.CInt:
return 0
# ============================================================
# RenumberSSA: 重编号函数内数字 SSA 名
#
# 解决 build_alloca_at_entry 在入口块终止指令前插入 alloca 导致
# SSA 名在文本输出中非单调递增的问题llc 报错:
# "instruction expected to be numbered '%N' or greater")。
#
# 现象: entry 块原指令 %0-%6 + bralloca_at_entry 插入 %28/%41 到 br 之前,
# 文本输出顺序为 entry(%0..%6, %28, %41) → if.then(%7..%27)
# 导致 %28 出现在 %7 之前llc 拒绝编译。
#
# 算法:
# 1. 扫描所有 BB 指令行,按文本输出顺序收集数字 SSA 名 %N
# 2. 按首次出现顺序建立 old→new 映射(从 0 递增)
# 3. 替换所有指令行文本中的 %N 为新编号
#
# 注意:
# - 仅重编号纯数字 SSA 名(%0, %1, ...
# - 命名 SSA 名(%entry, %.tmp, %self、块标签、全局符号 @N 不重编号
# - 仅对 define有函数体调用declare 无函数体无需处理
# - 当前 TransPyV 参数多为命名(%self/%env/%n非数字无需重编号参数
# ============================================================
# 判定字符是否为十进制数字
def _is_digit_ch(ch: t.CChar) -> t.CInt:
"""判断字符是否是 '0'-'9'
ch 是值类型 (t.CChar = i8)TPC 已修复值类型 is None 处理:
值类型 is None 恒为 false生成 i1 0 常量),不会解引用空指针。
"""
if ch is None:
return 0
if ch >= '0' and ch <= '9':
return 1
return 0
# ============================================================
# i32 数组的安全访问(显式字节偏移)
#
# TransPyC 的 ptr[i] 指针索引可能被当作字节索引i8*)而非 i32* 索引,
# 导致内存损坏。必须显式计算字节偏移addr = base + idx * 4再转型为 i32*。
# 参见 HandlesTranslator.py:358 的 t.CUInt64T(ptr) + offset * sizeof 用法。
# ============================================================
def _map_get_i32(map_arr: t.CChar | t.CPtr, idx: t.CInt) -> t.CInt:
"""读取 i32 数组 map_arr[idx](显式字节偏移 idx*4
使用 (t.CInt | t.CPtr)(uint64) 强转将字节偏移转为 i32*,再用 c.Deref 解引用。
TPC 已修复类型联合强转_HandleTypeUnionCastLlvm 添加 fallback此写法可用。
"""
if map_arr is None:
return -1
byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4
elem_ptr: t.CInt | t.CPtr = (t.CInt | t.CPtr)(byte_off)
return c.Deref(elem_ptr)
def _map_set_i32(map_arr: t.CChar | t.CPtr, idx: t.CInt, val: t.CInt):
"""写入 map_arr[idx] = vali32 数组,显式字节偏移 idx*4
使用 (t.CInt | t.CPtr)(uint64) 强转 + c.DerefAs 解引用赋值。
TPC 已修复类型联合强转,此写法可用。
"""
if map_arr is None:
return
byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4
elem_ptr: t.CInt | t.CPtr = (t.CInt | t.CPtr)(byte_off)
c.DerefAs(elem_ptr, val)
def _scan_max_ssa_in_text(text: t.CChar | t.CPtr, cur_max: t.CInt) -> t.CInt:
"""扫描文本,找出 %NN 为数字)中最大的 N与 cur_max 比较返回更大值
仅匹配 '%' 后紧跟数字的情况,跳过命名 SSA 名(%entry 等)。
"""
result: t.CInt = cur_max
p: t.CChar | t.CPtr = text
while p is not None and p[0] != '\0':
if p[0] == '%':
nxt: t.CChar | t.CPtr = p + 1
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
num: t.CInt = string.atoi(nxt)
if num > result:
result = num
p = p + 1
return result
def _find_max_ssa_number(func: Function | t.CPtr) -> t.CInt:
"""遍历所有 BB 指令行,找出最大的 %N 数字;无数字 SSA 名返回 -1"""
max_n: t.CInt = -1
if func is None or func.Blocks is None:
return max_n
blk: BasicBlock | t.CPtr = func.Blocks.Head
while blk is not None:
if blk.Lines is not None:
line: Line | t.CPtr = blk.Lines.Head
while line is not None:
if line.Buf is not None:
max_n = _scan_max_ssa_in_text(line.Buf, max_n)
line = line.Next
blk = blk.Next
return max_n
def _assign_new_numbers(text: t.CChar | t.CPtr,
map_arr: t.CChar | t.CPtr,
max_n: t.CInt, next_new: t.CInt) -> t.CInt:
"""扫描文本,对每个 %N数字若 map_arr[N]==-1 则分配 next_new
返回更新后的 next_new。按文本首次出现顺序建立映射。
"""
result: t.CInt = next_new
p: t.CChar | t.CPtr = text
while p is not None and p[0] != '\0':
if p[0] == '%':
nxt: t.CChar | t.CPtr = p + 1
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
num: t.CInt = string.atoi(nxt)
if num >= 0 and num <= max_n:
if _map_get_i32(map_arr, num) == -1:
_map_set_i32(map_arr, num, result)
result += 1
p = p + 1
return result
def _replace_ssa_numbers(text: t.CChar | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
map_arr: t.CChar | t.CPtr,
max_n: t.CInt) -> t.CChar | t.CPtr:
"""构建新文本,将 %N 替换为映射后的新编号
逐字符扫描:遇到 %N数字查映射表替换其他字符原样复制。
返回新分配的缓冲区(调用方无需释放旧 Bufpool 统一管理)。
"""
src_len: t.CSizeT = string.strlen(text)
# 新缓冲区:最坏情况每个数字变长(位数增加),分配 src_len*2 + 64
new_size: t.CSizeT = src_len * 2 + 64
new_buf: t.CChar | t.CPtr = pool.alloc(new_size)
if new_buf is None:
return None
# 一次性分配数字缓冲区(循环中复用)
num_buf: t.CChar | t.CPtr = pool.alloc(16)
if num_buf is None:
new_buf[0] = '\0'
return new_buf
dst_idx: t.CSizeT = 0
src_p: t.CChar | t.CPtr = text
while src_p is not None and src_p[0] != '\0':
if dst_idx + 16 >= new_size:
# 缓冲区不足,截断
break
if src_p[0] == '%':
nxt: t.CChar | t.CPtr = src_p + 1
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
num: t.CInt = string.atoi(nxt)
new_num: t.CInt = _map_get_i32(map_arr, num)
if num >= 0 and num <= max_n and new_num != -1:
# 写入 '%'
new_buf[dst_idx] = '%'
dst_idx += 1
# 写入新编号
num_buf[0] = '\0'
viperlib.snprintf(num_buf, 16, "%d", new_num)
k: t.CSizeT = 0
while num_buf[k] != '\0' and dst_idx + 1 < new_size:
new_buf[dst_idx] = num_buf[k]
dst_idx += 1
k += 1
# 跳过旧数字字符
src_p = src_p + 1
while src_p is not None and _is_digit_ch(src_p[0]) != 0:
src_p = src_p + 1
continue
# 普通字符:直接复制
new_buf[dst_idx] = src_p[0]
dst_idx += 1
src_p = src_p + 1
new_buf[dst_idx] = '\0'
return new_buf
def RenumberSSA(func: Function | t.CPtr, pool: memhub.MemBuddy | t.CPtr):
"""重编号函数内所有数字 SSA 名,确保按文本输出顺序单调递增
在 FunctionPrint 序列化函数体之前调用。
仅对 defineIsDeclared==0且 Blocks 非空 的函数生效。
"""
if func is None or pool is None:
return
if func.IsDeclared == 1:
return # declare 无函数体
if func.Blocks is None:
return
# 1. 第一遍扫描:找出最大 SSA 编号
max_n: t.CInt = _find_max_ssa_number(func)
if max_n < 0:
return # 无数字 SSA 名
# 2. 分配并初始化映射数组 map_arr[0..max_n] = -1
# 用 i8* 存储pool.alloc 返回 i8*),通过 _map_get_i32/_map_set_i32 访问。
# 初始化为 -1每个 i32 元素 4 字节0xFFFFFFFF = -1有符号 i32
# 用 memset 一次性填充,避免循环内 ptr[i] 指针索引问题。
map_bytes: t.CSizeT = (max_n + 1) * 4 # t.CInt = 4 字节
map_arr: t.CChar | t.CPtr = pool.alloc(map_bytes)
if map_arr is None:
return
string.memset(map_arr, 0xFF, map_bytes) # 0xFF * 4 = 0xFFFFFFFF = -1
# 3. 第二遍扫描:按文本顺序分配新编号
next_new: t.CInt = 0
blk: BasicBlock | t.CPtr = func.Blocks.Head
while blk is not None:
if blk.Lines is not None:
line: Line | t.CPtr = blk.Lines.Head
while line is not None:
if line.Buf is not None:
next_new = _assign_new_numbers(line.Buf, map_arr, max_n, next_new)
line = line.Next
blk = blk.Next
# 4. 检查是否有需要重编号的old != new
need_renumber: t.CInt = 0
i: t.CInt = 0
while i <= max_n:
v: t.CInt = _map_get_i32(map_arr, i)
if v != -1 and v != i:
need_renumber = 1
break
i += 1
if need_renumber == 0:
return # 编号已单调,无需替换
# 5. 第三遍扫描:替换所有指令行文本
blk = func.Blocks.Head
while blk is not None:
if blk.Lines is not None:
line: Line | t.CPtr = blk.Lines.Head
while line is not None:
if line.Buf is not None:
new_buf: t.CChar | t.CPtr = _replace_ssa_numbers(
line.Buf, pool, map_arr, max_n)
if new_buf is not None:
line.Buf = new_buf
line.Len = string.strlen(new_buf)
line = line.Next
blk = blk.Next
# ============================================================
# FunctionPrint: 将函数序列化为 IR 文本
#
@@ -293,6 +588,12 @@ def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
"""将函数 func 序列化为 IR 文本写入 buf"""
if func is None or buf is None or size == 0: return
# 在序列化前重编号数字 SSA 名,确保按文本输出顺序单调递增。
# 解决 build_alloca_at_entry 在入口块终止指令前插入 alloca 导致
# SSA 名非单调llc 报错 "instruction expected to be numbered '%N' or greater")。
# RenumberSSA 内部会跳过 declare 函数IsDeclared==1无需在此判断。
RenumberSSA(func, pool)
ty_buf: t.CChar | t.CPtr = pool.alloc(128)
if ty_buf is None: return
ty_buf[0] = '\0'
@@ -371,12 +672,22 @@ def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
line: Line | t.CPtr = None
if blk.Lines is not None:
line = blk.Lines.Head
has_lines: t.CInt = 0
while line is not None:
if line.Buf is not None:
_append_cstr(buf, size, " ")
_append_cstr(buf, size, line.Buf)
_append_cstr(buf, size, "\n")
has_lines = 1
line = line.Next
# 块未终止兜底LLVM IR 要求每个基本块以终止指令br/ret/unreachable结尾。
# 两种情况需要补 unreachable
# 1. 空块has_lines==0标签后无任何指令
# 2. 有指令但未终止IsTerminated==0如 break/continue 后的 dead.N 块
# 被后续语句填入指令但缺少终止符,导致下一个标签处报
# "expected instruction opcode"
if has_lines == 0 or blk.IsTerminated == 0:
_append_cstr(buf, size, " unreachable\n")
blk = blk.Next
_append_cstr(buf, size, "}\n")

View File

@@ -39,7 +39,8 @@ from .__module import (
)
from .__builder import (
IRBuilder, new_builder, position_at_end,
build_alloca, build_load, build_store,
builder_cur_block_is_terminated,
build_alloca, 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,
build_fadd, build_fsub, build_fmul, build_fdiv, build_frem, build_fneg,

View File

@@ -5,7 +5,7 @@ import stdio
import viperlib
import memhub
from linkedlist import GSList, GSListNode
from .__types import LLVMType, TypePrint, Array, PrintStructDefinition, get_struct_name, Struct
from .__types import LLVMType, TypePrint, Array, PrintStructDefinition, get_struct_name, Struct, ParamNode
from .__function import Function, FunctionPrint, _ll_name_needs_quote, Param
@@ -150,39 +150,82 @@ def module_set_datalayout(mod: LLVMModule | t.CPtr, layout: t.CChar | t.CPtr):
# ============================================================
# 命名结构体类型管理
# ============================================================
# ============================================================
# _is_opaque_struct - 检查类型是否是 opaque 结构体
#
# opaque 结构体特征: Struct 类型,字段数为 0 且字段列表为 None。
# 通过 Struct(pool, None, 0, name) 创建IR 输出为 %"name" = type opaque。
# 用于在 module_add_named_type 中区分 opaque 声明和完整定义,
# 确保 opaque 声明插入链表头部(在使用之前声明)。
#
# Returns: 1=是 opaque / 0=否
# ============================================================
def _is_opaque_struct(ty: LLVMType | t.CPtr) -> int:
"""检查类型是否是 opaque 结构体(无字段的命名结构体)"""
if ty is None:
return 0
match ty:
case LLVMType.Struct(fields, fcount, name):
# opaque: 字段数为 0 且字段列表为 None
if fcount == 0 and fields is None:
return 1
return 0
case _:
return 0
def module_add_named_type(mod: LLVMModule | t.CPtr, pool: memhub.MemBuddy | t.CPtr,
ty: LLVMType | t.CPtr):
"""将命名结构体类型添加到模块(用于输出 %"name" = type { ... } 定义行)
非命名结构体Name 为 None 或非 Struct 类型)会被忽略。
按 Name 去重,相同名称的类型只保留第一个
按 Name 去重;同名时 opaque 声明可被完整定义替换(供跨模块值类型 alloca
"""
if mod is None or pool is None or ty is None:
return
name: t.CChar | t.CPtr = get_struct_name(ty)
if name is None:
return
# 去重检查
# 去重检查opaque 声明可被完整定义替换)
cur: NamedTypeNode | t.CPtr = mod.NamedTypeHead
while cur is not None:
if cur.Ty is not None:
existing_name: t.CChar | t.CPtr = get_struct_name(cur.Ty)
if existing_name is not None:
if string.strcmp(existing_name, name) == 0:
# 已有完整定义则无需替换
if _is_opaque_struct(cur.Ty) == 0:
return
# 已有 opaque新类型也是 opaque 则不重复
if _is_opaque_struct(ty) != 0:
return
# 用完整定义替换 opaque 声明(原地替换 Ty 指针)
cur.Ty = ty
return
cur = cur.Next
# 创建新节点并追加到链表尾部
# 创建新节点
node: NamedTypeNode | t.CPtr = pool.alloc(NAMED_TYPE_NODE_SIZE)
if node is None:
return
string.memset(node, 0, NAMED_TYPE_NODE_SIZE)
node.Ty = ty
node.Next = None
if mod.NamedTypeHead is None:
# opaque 类型插入链表头部,确保在引用它的完整定义之前声明。
# 例如 TypeRegistry{HashTable*, ...} 中HashTable 的 opaque 声明
# 必须在 TypeRegistry 定义之前,否则 llc 报 "use of undefined type"。
# 完整定义追加到链表尾部,保持原有顺序。
if _is_opaque_struct(ty) != 0:
node.Next = mod.NamedTypeHead
if mod.NamedTypeHead is None:
mod.NamedTypeTail = node
mod.NamedTypeHead = node
else:
mod.NamedTypeTail.Next = node
mod.NamedTypeTail = node
node.Next = None
if mod.NamedTypeHead is None:
mod.NamedTypeHead = node
else:
mod.NamedTypeTail.Next = node
mod.NamedTypeTail = node
mod.NamedTypeCount += 1
@@ -228,8 +271,24 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr,
opaque_ty: LLVMType | t.CPtr = Struct(pool, None, 0, sname)
if opaque_ty is not None:
module_add_named_type(mod, pool, opaque_ty)
# 递归扫描结构体字段中的跨模块引用
# 例如 TypeRegistry{HashTable*, MemManager*} 中的 HashTable 引用
if sfields is not None:
fld: ParamNode | t.CPtr = sfields
while fld is not None:
if fld.Ty is not None:
_scan_type_for_cross_module_refs(pool, mod, fld.Ty)
fld = fld.Next
case _:
_scan_type_for_cross_module_refs(pool, mod, pointee)
case LLVMType.Struct(sfields2, sfcount2, sname2):
# 顶层结构体(非指针):递归扫描字段
if sfields2 is not None:
fld2: ParamNode | t.CPtr = sfields2
while fld2 is not None:
if fld2.Ty is not None:
_scan_type_for_cross_module_refs(pool, mod, fld2.Ty)
fld2 = fld2.Next
case LLVMType.Array(elem_ty, acount):
if elem_ty is not None:
_scan_type_for_cross_module_refs(pool, mod, elem_ty)
@@ -333,12 +392,14 @@ def LLVMModulePrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
param_scan = param_scan.Next
func_scan = func_scan.Next
nt: NamedTypeNode | t.CPtr = mod.NamedTypeHead
# 结构体定义缓冲区48 字段 × ~50 字节/字段类型名 ≈ 2400 字节4096 足够
# 在循环外分配一次并复用,减少 pool 碎片
ty_buf: t.CChar | t.CPtr = pool.alloc(4096)
while nt is not None:
if nt.Ty is not None:
ty_buf: t.CChar | t.CPtr = pool.alloc(256)
if ty_buf is not None:
ty_buf[0] = '\0'
PrintStructDefinition(ty_buf, 256, nt.Ty, pool)
PrintStructDefinition(ty_buf, 4096, nt.Ty, pool)
_append_cstr(buf, size, ty_buf)
_append_cstr(buf, size, "\n")
nt = nt.Next

View File

@@ -3,6 +3,7 @@ from stdint import *
import viperlib
import string
import memhub
import stdio
from linkedlist import GSListNode
@@ -371,7 +372,15 @@ def TypePrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
case LLVMType.Int(bits):
viperlib.snprintf(buf, size, "i%d", bits)
case LLVMType.Ptr(pointee):
# 先打印 pointee再追加 '*'
# 快速路径: Ptr(Struct(name)) → %"name"*
# 使用 snprintf 一次性格式化,避免递归 TypePrint + _strappend 导致的截断
if pointee is not None:
match pointee:
case LLVMType.Struct(sfields, sfcount, sname):
if sname is not None:
viperlib.snprintf(buf, size, "%%\"%s\"*", sname)
return
# 通用路径: 递归打印 pointee 后追加 '*'
TypePrint(buf, size, pointee, pool)
pos: t.CSizeT = string.strlen(buf)
if pos + 1 < size:
@@ -380,19 +389,17 @@ def TypePrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
case LLVMType.Array(elem_ty, count):
# [N x elem_ty]
viperlib.snprintf(buf, size, "[%d x ", count)
tmp: t.CChar | t.CPtr = pool.alloc(128)
# SHA1 限定类型名(如 %"21a7fcfc665f75ef.NamedTypeNode")可达 ~50 字节,
# 嵌套指针/数组更易超过 128使用 512 字节避免截断
tmp: t.CChar | t.CPtr = pool.alloc(512)
if tmp is not None:
TypePrint(tmp, 128, elem_ty, pool)
TypePrint(tmp, 512, elem_ty, pool)
_strappend(buf, size, tmp)
_strappend(buf, size, "]")
case LLVMType.Struct(fields, fcount, name):
if name is not None:
# 命名结构体: %"name"
buf[0] = '%'
buf[1] = '"'
buf[2] = '\0'
_strappend(buf, size, name)
_strappend(buf, size, "\"")
viperlib.snprintf(buf, size, "%%\"%s\"", name)
else:
# 匿名结构体: {ty1, ty2, ...}
_print_struct_body(buf, size, fields, pool)
@@ -402,12 +409,13 @@ def TypePrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
_strappend(buf, size, " (")
cur2: ParamNode | t.CPtr = params
first2: t.CInt = 1
tmp3: t.CChar | t.CPtr = pool.alloc(128)
# 函数参数类型可能为 SHA1 限定命名结构体指针512 字节避免截断
tmp3: t.CChar | t.CPtr = pool.alloc(512)
while cur2 is not None:
if first2 == 0:
_strappend(buf, size, ", ")
if tmp3 is not None:
TypePrint(tmp3, 128, cur2.Ty, pool)
TypePrint(tmp3, 512, cur2.Ty, pool)
_strappend(buf, size, tmp3)
cur2 = cur2.Next
first2 = 0
@@ -457,21 +465,28 @@ def _strappend(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr)
# ============================================================
def _print_struct_body(buf: t.CChar | t.CPtr, size: t.CSizeT,
fields: ParamNode | t.CPtr, pool: memhub.MemBuddy | t.CPtr):
"""打印 {ty1, ty2, ...} 到 buf追加模式不覆盖已有内容"""
"""打印 {ty1, ty2, ...} 到 buf追加模式不覆盖已有内容
直接在 buf 当前末尾写入字段类型,避免 pool.alloc 返回与 buf 重叠的内存
导致 TypePrint 写入 tmp 时覆盖 buf 已有内容。
"""
if buf is None or size == 0:
return
_strappend(buf, size, "{")
cur: ParamNode | t.CPtr = fields
first: t.CInt = 1
tmp: t.CChar | t.CPtr = pool.alloc(128)
fidx: t.CInt = 0
while cur is not None:
if first == 0:
_strappend(buf, size, ", ")
if tmp is not None:
TypePrint(tmp, 128, cur.Ty, pool)
_strappend(buf, size, tmp)
# 直接在 buf 当前末尾位置写入字段类型
# TypePrint 首字节置 '\0' 是无害的pos 处本来就是 '\0'
pos: t.CSizeT = string.strlen(buf)
if pos + 2 < size:
TypePrint(buf + pos, size - pos, cur.Ty, pool)
cur = cur.Next
first = 0
fidx += 1
_strappend(buf, size, "}")
@@ -493,15 +508,12 @@ def PrintStructDefinition(buf: t.CChar | t.CPtr, size: t.CSizeT,
case LLVMType.Struct(fields, fcount, name):
if name is None:
return
buf[0] = '%'
buf[1] = '"'
buf[2] = '\0'
_strappend(buf, size, name)
# 使用 snprintf 构建 "%%"name" = type" 前缀,避免 _strappend 截断
# opaque 类型: fcount==0 且 fields is None跨模块前向声明
if fcount == 0 and fields is None:
_strappend(buf, size, "\" = type opaque")
viperlib.snprintf(buf, size, "%%\"%s\" = type opaque", name)
else:
_strappend(buf, size, "\" = type ")
viperlib.snprintf(buf, size, "%%\"%s\" = type ", name)
_print_struct_body(buf, size, fields, pool)
case _:
return

View File

@@ -429,6 +429,13 @@ class MemBuddy(MemManager):
if block is not None:
c.DerefAs(block, t.CVoid(t.CUInt64T((order << 1) | 1), t.CPtr))
result = t.CVoid(t.CUInt64T(block) + MEMBUDDY_HEADER_SIZE, t.CPtr)
if result is None:
# 分配失败: 打印内存使用情况
fb: t.CSizeT = self.free_bytes()
ub: t.CSizeT = self.usable_size - fb
stdio.printf("[MEM-FAIL] alloc(%zu) failed: total=%zu used=%zu free=%zu free_blocks=%zu\n",
size, self.usable_size, ub, fb, self.free_count())
stdio.fflush(0)
self._unlock()
return result
@@ -503,6 +510,32 @@ class MemBuddy(MemManager):
cur = t.CVoid(c.Deref(t.CUInt64T(cur, t.CPtr)), t.CPtr)
return count
def free_bytes(self) -> t.CSizeT:
# 统计所有空闲块的总字节数
total: t.CSizeT = 0
i: t.CInt
for i in range(MEMBUDDY_MAX_ORDERS + 1):
head_val: t.CUInt64T = self.free_lists[i]
cur: t.CVoid | t.CPtr = t.CVoid(head_val, t.CPtr)
while t.CUInt64T(cur) != 0:
total += _block_size_at_order(i)
cur = t.CVoid(c.Deref(t.CUInt64T(cur, t.CPtr)), t.CPtr)
return total
def used_bytes(self) -> t.CSizeT:
# 已使用字节数 = 可用区总大小 - 空闲字节数
return self.usable_size - self.free_bytes()
def dump_stats(self, label: t.CChar | t.CPtr):
# 打印内存使用统计
fb: t.CSizeT = self.free_bytes()
ub: t.CSizeT = self.usable_size - fb
stdio.printf("[MEM] %s: total=%zu used=%zu (%zu%%) free=%zu free_blocks=%zu\n",
label, self.usable_size, ub,
(ub * 100) / self.usable_size if self.usable_size > 0 else 0,
fb, self.free_count())
stdio.fflush(0)
def self_check(self) -> t.CInt:
# 验证伙伴分配器内部一致性,返回 0=OK非 0=损坏
if self.usable is None:

View File

@@ -51,6 +51,9 @@ class File:
self.is_append = False
self._share_mode = share
stdio.printf("[FILE] __init__ filename=%s mode=%d share=%d\n", filename, mode, share)
stdio.fflush(0)
access: ULONG = 0
disposition: ULONG = w32.win32file.OPEN_EXISTING
@@ -97,12 +100,18 @@ class File:
disposition = w32.win32file.OPEN_EXISTING
self.can_read = True
stdio.printf("[FILE] before CreateFileA access=%d disp=%d share=%d\n", access, disposition, self._share_mode)
stdio.fflush(0)
self.handle = w32.win32file.CreateFileA(
filename, access, self._share_mode,
None, disposition, w32.win32file.FILE_ATTRIBUTE_NORMAL, None
)
stdio.printf("[FILE] after CreateFileA handle=%p\n", self.handle)
stdio.fflush(0)
if self.handle == w32.win32base.INVALID_HANDLE_VALUE:
stdio.printf("[FILE] FAIL: handle == INVALID_HANDLE_VALUE\n")
stdio.fflush(0)
self.closed = True
return

View File

@@ -5,7 +5,7 @@ HANDLE: t.CTypedef = VOIDPTR
LPCSTR: t.CTypedef = t.CConst | t.CChar | t.CPtr
LPCWSTR: t.CTypedef = t.CConst | t.CUnsignedShort | t.CPtr
INVALID_HANDLE_VALUE: t.CDefine = t.CVoid(-1)
INVALID_HANDLE_VALUE: t.CDefine = 0xFFFFFFFF
NULL: t.CDefine = 0
TRUE: t.CDefine = 1
FALSE: t.CDefine = 0