import t, c
from stdint import *
import string
import viperlib
import memhub
import stdio
from linkedlist import GSListNode, GSList
from .__types import LLVMType, ParamNode, TypePrint


# ============================================================
# Line: 指令文本行
#
# 每条 LLVM IR 指令序列化为一行文本，通过链表组织。
# 继承 GSListNode[Line] 获取强类型 Next: Line|CPtr
#
# 注意: 必须加 @t.NoVTable。否则 TransPyC 为子类添加 vtable 指针(T*)，
# 但完全丢失父类 GSListNode[T] 的 Next 字段，导致 struct 布局错位，
# 所有字段访问读取垃圾值（见续 XV 会话根因分析）。
# ============================================================
@t.NoVTable
class Line(GSListNode[Line]):
    Buf: t.CChar | t.CPtr       # 行文本（NUL 结尾）
    Len: t.CSizeT               # 文本长度（不含 NUL）


# ============================================================
# BasicBlock: 基本块
#
# 持有名称 + 指令行链表 + 终止指令标志。
# 继承 GSListNode[BasicBlock] 获取强类型 Next: BasicBlock|CPtr
# Lines 为 GSList[Line] 指针，提供 O(1) 追加。
#
# 注意: 必须加 @t.NoVTable（理由同 Line）。
# ============================================================
@t.NoVTable
class BasicBlock(GSListNode[BasicBlock]):
    Name: t.CChar | t.CPtr          # 块名（如 "entry"/"if.then"）
    Lines: GSList[Line] | t.CPtr    # 指令行链表容器（Head/Tail/Count）
    IsTerminated: t.CInt            # 1=已有终止指令（br/ret）


# ============================================================
# Param: 函数参数
#
# 继承 GSListNode[Param] 获取强类型 Next: Param|CPtr
# Attrs 存储参数属性文本（如 "noundef"/"nocapture"/"readonly"），None=无属性
#
# 注意: 必须加 @t.NoVTable（理由同 Line）。
# ============================================================
@t.NoVTable
class Param(GSListNode[Param]):
    Ty: LLVMType | t.CPtr           # 参数类型
    Name: t.CChar | t.CPtr          # 参数名（如 "%0"/"%a"）
    Attrs: t.CChar | t.CPtr         # 参数属性文本（None=无属性）


# ============================================================
# Function: 函数
#
# 持有名称 + 返回类型 + 参数链表 + 基本块链表。
# 继承 GSListNode[Function] 获取强类型 Next: Function|CPtr
# Params/Blocks 为 GSList 指针，提供 O(1) 追加。
# Attrs 存储函数级属性文本（如 "nounwind"/"noinline"），None=无属性
#
# 注意: 必须加 @t.NoVTable（理由同 Line）。
# ============================================================
@t.NoVTable
class Function(GSListNode[Function]):
    Name: t.CChar | t.CPtr              # 函数名
    RetTy: LLVMType | t.CPtr            # 返回类型
    Params: GSList[Param] | t.CPtr      # 参数链表容器
    Blocks: GSList[BasicBlock] | t.CPtr # 基本块链表容器
    IsDeclared: t.CInt                  # 1=仅声明(declare), 0=定义(define)
    IsVarArg: t.CInt                    # 1=变参函数(...)
    Attrs: t.CChar | t.CPtr             # 函数属性文本（None=无属性）


# ============================================================
# 工厂函数
# ============================================================
# Line 硬编码大小: Next(8) + Buf(8) + Len(8) = 24 字节
LINE_SIZE: t.CDefine = 24

def new_line(pool: memhub.MemBuddy | t.CPtr, text: t.CChar | t.CPtr) -> Line | t.CPtr:
    """创建一个指令行，复制 text 到新分配的缓冲区"""
    ptr: Line | t.CPtr = pool.alloc(LINE_SIZE)
    if ptr is None: return None
    string.memset(ptr, 0, LINE_SIZE)
    if text is not None:
        slen: t.CSizeT = string.strlen(text)
        buf: t.CChar | t.CPtr = pool.alloc(slen + 1)
        if buf is not None:
            string.strcpy(buf, text)
            ptr.Buf = buf
            ptr.Len = slen
    return ptr


def _new_gslist(pool: memhub.MemBuddy | t.CPtr, list_size: t.CSizeT) -> t.CPtr:
    """从 pool 分配并零初始化一个 GSList 容器（Head/Tail/Count 全零）"""
    gsl: t.CPtr = pool.alloc(list_size)
    if gsl is None: return None
    string.memset(gsl, 0, list_size)
    return gsl


# BasicBlock 硬编码大小: Next(8) + Name(8) + Lines(8) + IsTerminated(4) = 32 字节
BLOCK_SIZE: t.CDefine = 32

def new_basic_block(pool: memhub.MemBuddy | t.CPtr, name: t.CChar | t.CPtr) -> BasicBlock | t.CPtr:
    """创建一个基本块"""
    ptr: BasicBlock | t.CPtr = pool.alloc(BLOCK_SIZE)
    if ptr is None: return None
    string.memset(ptr, 0, BLOCK_SIZE)
    ptr.Name = name
    ptr.Lines = _new_gslist(pool, 24)  # GSList: Head+Tail+Count
    ptr.IsTerminated = 0
    return ptr


# Param 硬编码大小: Next(8) + Ty(8) + Name(8) + Attrs(8) = 32 字节
PARAM_SIZE: t.CDefine = 32

def new_param(pool: memhub.MemBuddy | t.CPtr, ty: LLVMType | t.CPtr,
              name: t.CChar | t.CPtr) -> Param | t.CPtr:
    """创建一个函数参数"""
    ptr: Param | t.CPtr = pool.alloc(PARAM_SIZE)
    if ptr is None: return None
    string.memset(ptr, 0, PARAM_SIZE)
    ptr.Ty = ty
    ptr.Name = name
    ptr.Attrs = None
    return ptr


# Function 硬编码大小: Next(8) + Name(8) + RetTy(8) + Params(8) + Blocks(8) + IsDeclared(4) + IsVarArg(4) + Attrs(8) = 56 字节
FUNC_SIZE: t.CDefine = 56

def new_function(pool: memhub.MemBuddy | t.CPtr, name: t.CChar | t.CPtr,
                 ret_ty: LLVMType | t.CPtr) -> Function | t.CPtr:
    """创建一个函数"""
    ptr: Function | t.CPtr = pool.alloc(FUNC_SIZE)
    if ptr is None: return None
    string.memset(ptr, 0, FUNC_SIZE)
    ptr.Name = name
    ptr.RetTy = ret_ty
    ptr.Params = _new_gslist(pool, 24)  # GSList: Head+Tail+Count
    ptr.Blocks = _new_gslist(pool, 24)
    ptr.IsDeclared = 0
    ptr.Attrs = None
    return ptr


# ============================================================
# 链表操作（委托 GSList.append）
# ============================================================
def function_add_param(func: Function | t.CPtr, param: Param | t.CPtr):
    """将参数追加到函数参数链表"""
    if func is None or param is None: return
    if func.Params is None: return
    func.Params.append(param)


def function_add_block(func: Function | t.CPtr, block: BasicBlock | t.CPtr):
    """将基本块追加到函数块链表"""
    if func is None or block is None: return
    if func.Blocks is None: return
    func.Blocks.append(block)


def function_move_block_to_end(func: Function | t.CPtr, block: BasicBlock | t.CPtr):
    """将 block 移动到函数块链表末尾（保证 BB 文本顺序与控制流顺序一致）。

    用于解决 SSA 名非单调问题：BB 按创建顺序排列，但指令按控制流顺序生成，
    导致 SSA 编号在文本输出时非单调递增，llc 报错。
    在 position_at_end 时调用此函数，使 BB 顺序与 position 顺序一致。

    注意：必须使用 GSList[BasicBlock] 带类型参数，否则 TransPyC 创建不完整
    struct 类型，导致 blocks.Head/Tail/Count 字段访问读取垃圾值。
    """
    if func is None or block is None: return
    blocks: GSList[BasicBlock] | t.CPtr = func.Blocks
    if blocks is None: return

    # 已经是 Tail，无需移动
    if blocks.Tail is block:
        return

    # 从链表中移除 block
    if blocks.Head is block:
        # block 是 Head，直接后移 Head
        blocks.Head = block.Next
    else:
        # 遍历找前驱
        prev: BasicBlock | t.CPtr = blocks.Head
        while prev is not None and prev.Next is not block:
            prev = prev.Next
        if prev is None:
            # block 不在链表中，直接 append
            blocks.append(block)
            return
        prev.Next = block.Next

    # block.Next 由移除逻辑处理，重置为 None
    block.Next = None

    # append 到末尾（不调用 blocks.append 以避免 Count 重复递增）
    if blocks.Tail is not None:
        blocks.Tail.Next = block
    blocks.Tail = block


def block_append_line(block: BasicBlock | t.CPtr, line: Line | t.CPtr):
    """将指令行追加到基本块"""
    if block is None or line is None: return
    if block.Lines is None: return
    block.Lines.append(line)


def block_set_terminated(block: BasicBlock | t.CPtr, val: t.CInt):
    """设置块的终止标志（供跨模块调用，避免链式属性访问的 opaque 类型问题）"""
    if block is None: return
    block.IsTerminated = val


def block_get_terminated(block: BasicBlock | t.CPtr) -> t.CInt:
    """获取块的终止标志（供跨模块调用，避免链式属性访问的 opaque 类型问题）"""
    if block is None:
        return 1
    return block.IsTerminated


def block_append_text(pool: memhub.MemBuddy | t.CPtr, block: BasicBlock | t.CPtr,
                      text: t.CChar | t.CPtr):
    """创建指令行并追加到基本块"""
    if block is None or text is None: return
    line: Line | t.CPtr = new_line(pool, text)
    if line is not None:
        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


# ============================================================
# 属性设置
# ============================================================
def function_set_attrs(func: Function | t.CPtr, attrs: t.CChar | t.CPtr):
    """设置函数级属性文本（如 "nounwind"/"noinline"）"""
    if func is None: return
    func.Attrs = attrs


def param_set_attrs(param: Param | t.CPtr, attrs: t.CChar | t.CPtr):
    """设置参数属性文本（如 "noundef"/"nocapture"/"readonly"）"""
    if param is None: return
    param.Attrs = attrs


# ============================================================
# _ll_name_needs_quote - 检查 LLVM 标识符是否需要加引号
#
# LLVM IR 规范：标识符合法字符集为 [-a-zA-Z$._][-a-zA-Z$._0-9]*
# 以数字开头或含非合法字符（如 '['、']'、' '、'@' 等）时必须用双引号包围。
# '.' 是合法字符，不需要加引号（如 @.str、@llvm.memcpy 都不加引号）。
# SHA1 命名空间的函数名（如 "4dad2ba72ed7ba09.GSListNode[Param]"）含 '['、']'，
# 需要加引号；但纯 hex+点 的名字（如 "abc.def"）不需要。
# ============================================================
def _ll_name_needs_quote(name: str) -> t.CInt:
    """检查 LLVM 标识符是否需要加引号（以数字开头或含非合法字符）

    LLVM IR 合法字符：a-z, A-Z, 0-9, -, $, ., _
    首字符是数字时需要加引号（LLVM 要求局部/全局标识符首字符非数字，除非加引号）。
    含其他字符（如 '['、']'、空格）时需要加引号。
    """
    if name is None:
        return 0
    c0: t.CChar = name[0]
    if c0 >= '0' and c0 <= '9':
        return 1
    name_len: t.CSizeT = string.strlen(name)
    i: t.CSizeT = 0
    while i < name_len:
        ch: t.CChar = name[i]
        # 合法字符：a-z, A-Z, 0-9, -, $, ., _
        if not ((ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or
                (ch >= '0' and ch <= '9') or ch == '-' or ch == '$' or
                ch == '.' or ch == '_'):
            return 1
        i += 1
    return 0


# ============================================================
# RenumberSSA: 重编号函数内数字 SSA 名
#
# 解决 build_alloca_at_entry 在入口块终止指令前插入 alloca 导致
# SSA 名在文本输出中非单调递增的问题（llc 报错:
# "instruction expected to be numbered '%N' or greater"）。
#
# 现象: entry 块原指令 %0-%6 + br，alloca_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] = val（i32 数组，显式字节偏移 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:
    """扫描文本，找出 %N（N 为数字）中最大的 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（数字）查映射表替换，其他字符原样复制。
    返回新分配的缓冲区（调用方无需释放旧 Buf，pool 统一管理）。
    """
    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 序列化函数体之前调用。
    仅对 define（IsDeclared==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 文本
#
# 格式:
#   define <ret_ty> @<name>(<params>) {
#   <block_name>:
#     <instruction>
#     <instruction>
#   <block_name>:
#     <instruction>
#   }
# ============================================================
def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT,
                  func: Function | t.CPtr, pool: memhub.MemBuddy | t.CPtr):
    """将函数 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'

    # define <ret_ty> @<name>(<params>)
    if func.IsDeclared == 1:
        _append_cstr(buf, size, "declare ")
    else:
        _append_cstr(buf, size, "define ")
    ty_buf[0] = '\0'
    TypePrint(ty_buf, 128, func.RetTy, pool)
    _append_cstr(buf, size, ty_buf)
    _append_cstr(buf, size, " @")
    if func.Name is not None:
        if _ll_name_needs_quote(func.Name) != 0:
            _append_cstr(buf, size, "\"")
            _append_cstr(buf, size, func.Name)
            _append_cstr(buf, size, "\"")
        else:
            _append_cstr(buf, size, func.Name)
    _append_cstr(buf, size, "(")

    # 参数列表（从 GSList[Param].Head 遍历）
    cur: Param | t.CPtr = None
    if func.Params is not None:
        cur = func.Params.Head
    first: t.CInt = 1
    while cur is not None:
        if first == 0:
            _append_cstr(buf, size, ", ")
        # 参数属性（如 "noundef"/"nocapture"）在类型之前
        if cur.Attrs is not None:
            _append_cstr(buf, size, cur.Attrs)
            _append_cstr(buf, size, " ")
        ty_buf[0] = '\0'
        TypePrint(ty_buf, 128, cur.Ty, pool)
        _append_cstr(buf, size, ty_buf)
        # declare 和 define 都打印参数名（如果有）
        if cur.Name is not None:
            _append_cstr(buf, size, " ")
            # LLVM IR 要求参数名以 % 开头
            if cur.Name[0] != '%':
                _append_cstr(buf, size, "%")
            _append_cstr(buf, size, cur.Name)
        cur = cur.Next
        first = 0
    # 变参函数追加 ...
    if func.IsVarArg == 1:
        if func.Params is not None and func.Params.Head is not None:
            _append_cstr(buf, size, ", ...")
        else:
            _append_cstr(buf, size, "...")
    _append_cstr(buf, size, ")")

    # 函数级属性（如 "nounwind"/"noinline"）在 ) 之后
    if func.Attrs is not None:
        _append_cstr(buf, size, " ")
        _append_cstr(buf, size, func.Attrs)

    if func.IsDeclared == 1:
        _append_cstr(buf, size, "\n")
        return

    # 函数体
    _append_cstr(buf, size, " {\n")

    blk: BasicBlock | t.CPtr = None
    if func.Blocks is not None:
        blk = func.Blocks.Head
    while blk is not None:
        # 块标签
        if blk.Name is not None:
            _append_cstr(buf, size, blk.Name)
            _append_cstr(buf, size, ":\n")
        # 指令行（从 GSList[Line].Head 遍历）
        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")


# ============================================================
# 内部辅助
# ============================================================
def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr):
    """将 C 字符串 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'


# ============================================================
# 字段访问器（供其他模块绕过 stub 类型限制使用）
#
# 跨模块编译时，使用方模块只有 Function/Param 的 stub 类型
# （字段不足且字段类型可能错误），导致 TransPyC 静默跳过
# 字段访问（不生成 GEP，不报错），字段值永远为 null。
#
# 这些访问器在拥有完整类型定义的本模块内执行，能正确访问
# 所有字段。其他模块通过函数调用获取字段值，绕过 stub 限制。
# ============================================================
def function_get_name(func: Function | t.CPtr) -> t.CChar | t.CPtr:
    """获取函数名"""
    if func is None:
        return None
    return func.Name


def function_get_ret_ty(func: Function | t.CPtr) -> LLVMType | t.CPtr:
    """获取函数返回类型"""
    if func is None:
        return None
    return func.RetTy


def function_get_params(func: Function | t.CPtr) -> GSList[Param] | t.CPtr:
    """获取函数参数链表容器"""
    if func is None:
        return None
    return func.Params


def function_get_param_head(func: Function | t.CPtr) -> Param | t.CPtr:
    """获取函数参数链表头节点（一步到位，避免调用方访问 GSList.Head）"""
    if func is None:
        return None
    if func.Params is None:
        return None
    return func.Params.Head


def function_get_next(func: Function | t.CPtr) -> Function | t.CPtr:
    """获取链表下一个函数"""
    if func is None:
        return None
    return func.Next


def function_is_declared(func: Function | t.CPtr) -> t.CInt:
    """获取函数是否为声明（1=declare 仅声明, 0=define 定义）"""
    if func is None:
        return 0
    return func.IsDeclared


def param_get_name(param: Param | t.CPtr) -> t.CChar | t.CPtr:
    """获取参数名"""
    if param is None:
        return None
    return param.Name


def param_get_ty(param: Param | t.CPtr) -> LLVMType | t.CPtr:
    """获取参数类型"""
    if param is None:
        return None
    return param.Ty


def param_get_next(param: Param | t.CPtr) -> Param | t.CPtr:
    """获取链表下一个参数"""
    if param is None:
        return None
    return param.Next
