diff --git a/includes/llvmlite/__builder.vp b/includes/llvmlite/__builder.vp index 3899ff3..2d761d0 100644 --- a/includes/llvmlite/__builder.vp +++ b/includes/llvmlite/__builder.vp @@ -25,7 +25,7 @@ from .__function import Function, BasicBlock, block_append_text, block_set_termi # builder.build_ret(r) # # 注意: 加 @t.NoVTable 以与硬编码 BUILDER_SIZE=32 保持一致 -# (所有方法均为顶层函数,无需 vtable)。 +# (方法以实例方法实现,无虚表;文件末尾的同名顶层函数为一行委托)。 # ============================================================ @t.NoVTable class IRBuilder: @@ -34,6 +34,1098 @@ class IRBuilder: CurBlock: BasicBlock | t.CPtr # 当前插入点 Counter: t.CInt # SSA 名计数器 + # ============================================================ + # 内部辅助 + # ============================================================ + def _alloc_ssa_name(self) -> t.CChar | t.CPtr: + """分配一个 SSA 名字符串 "%N" 并递增计数器""" + pool: memhub.MemBuddy | t.CPtr = self.Pool + buf: t.CChar | t.CPtr = pool.alloc(16) + if buf is None: return None + viperlib.snprintf(buf, 16, "%%%d", self.Counter) + self.Counter += 1 + return buf + + + def _type_str(self, ty: LLVMType | t.CPtr) -> t.CChar | t.CPtr: + """获取类型的 IR 文本(从 pool 分配 512 字节缓冲区) + + 注意:复杂结构体类型(如 13 方法的虚表 {i8*,i8*,...})可能超过 64 字节, + 必须使用足够大的缓冲区,否则 '}' 会被截断导致 IR 语法错误。 + """ + pool: memhub.MemBuddy | t.CPtr = self.Pool + buf: t.CChar | t.CPtr = pool.alloc(512) + if buf is None: return None + buf[0] = '\0' + TypePrint(buf, 512, ty, pool) + return buf + + + def _emit(self, text: t.CChar | t.CPtr): + """将指令文本追加到当前块""" + if self is None or self.CurBlock is None: return + block_append_text(self.Pool, self.CurBlock, text) + + + def _mark_terminated(self): + """标记当前块已终止""" + if self is None or self.CurBlock is None: return + block_set_terminated(self.CurBlock, 1) + + + def _build_binop(self, op: t.CChar | t.CPtr, + lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: + """二元运算通用实现""" + if self is None or lhs is None or rhs is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(lhs.Ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = %s %s %s, %s", name, op, ty_s, lhs.Name, rhs.Name) + self._emit(line) + return SSAValue(pool, lhs.Ty, name) + + + # ============================================================ + # 块定位 + # ============================================================ + def position_at_end(self, block: BasicBlock | t.CPtr): + """将插入点设为 block 的末尾,并把 block 移到函数块链表末尾。 + + 移动 block 是为了保证 BB 文本输出顺序与控制流(position)顺序一致, + 避免 SSA 名在文本中非单调递增导致 llc 编译失败。 + """ + if self is None: return + self.CurBlock = block + if self.Func is not None: + function_move_block_to_end(self.Func, block) + + + def builder_cur_block_is_terminated(self) -> t.CInt: + """检查当前块的 IsTerminated 标志 + + 通过 block_get_terminated 辅助函数访问,避免链式属性 + self.CurBlock.IsTerminated 在跨模块场景下 BasicBlock 为 opaque + 导致的 GEP 静默跳过问题。 + """ + if self is None or self.CurBlock is None: + return 1 + return block_get_terminated(self.CurBlock) + + + # ============================================================ + # 内存指令 + # ============================================================ + def build_alloca(self, ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = alloca + + 返回类型为 ty* 的 SSA 值(默认对齐,由后端决定)。 + 注意:TPC 并行编译时 include 模块的函数默认参数不会预注册(argN 名回退), + 因此本函数不设默认参数;需要显式对齐请用 build_alloca_aligned。 + """ + if self is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(ty) + line: t.CChar | t.CPtr = pool.alloc(128) + if line is None: return None + viperlib.snprintf(line, 128, "%s = alloca %s", name, ty_s) + self._emit(line) + + # 结果类型是 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_alloca_aligned(self, ty: LLVMType | t.CPtr, + align: int) -> Value | t.CPtr: + """%N = alloca , align N + + 返回类型为 ty* 的 SSA 值;显式指定对齐(如 va_list 24 字节缓冲区需 16 对齐)。 + align 不设默认值(TPC 并行编译时 include 默认参数不预注册),调用方必须显式传入。 + """ + if self is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(ty) + line: t.CChar | t.CPtr = pool.alloc(128) + if line is None: return None + viperlib.snprintf(line, 128, "%s = alloca %s, align %d", name, ty_s, align) + self._emit(line) + + # 结果类型是 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_alloca_at_entry(self, ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = alloca (在函数入口块生成,确保支配性) + + 在入口块的终止指令之前插入 alloca,避免在条件分支内生成 alloca + 导致的支配性违规(Instruction does not dominate all uses)。 + """ + if self is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + func: Function | t.CPtr = self.Func + if func is None or func.Blocks is None: + return self.build_alloca(ty) + entry_blk: BasicBlock | t.CPtr = func.Blocks.Head + if entry_blk is None: + return self.build_alloca(ty) + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(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(self, ty: LLVMType | t.CPtr, + ptr: Value | t.CPtr) -> Value | t.CPtr: + """%N = load , """ + if self is None or ptr is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(ty) + ptr_ty_s: t.CChar | t.CPtr = self._type_str(ptr.Ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = load %s, %s %s", name, ty_s, ptr_ty_s, ptr.Name) + self._emit(line) + return SSAValue(pool, ty, name) + + + def build_store(self, val: Value | t.CPtr, ptr: Value | t.CPtr): + """store , """ + if self is None or val is None or ptr is None: return + pool: memhub.MemBuddy | t.CPtr = self.Pool + val_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + ptr_ty_s: t.CChar | t.CPtr = self._type_str(ptr.Ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return + viperlib.snprintf(line, 256, "store %s %s, %s %s", val_ty_s, val.Name, ptr_ty_s, ptr.Name) + self._emit(line) + + + # ============================================================ + # 二元运算指令 + # ============================================================ + def build_add(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = add , """ + return self._build_binop("add", lhs, rhs) + + + def build_sub(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = sub , """ + return self._build_binop("sub", lhs, rhs) + + + def build_mul(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = mul , """ + return self._build_binop("mul", lhs, rhs) + + + def build_sdiv(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = sdiv , """ + return self._build_binop("sdiv", lhs, rhs) + + + def build_udiv(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = udiv , """ + return self._build_binop("udiv", lhs, rhs) + + + def build_srem(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = srem , """ + return self._build_binop("srem", lhs, rhs) + + + def build_urem(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = urem , """ + return self._build_binop("urem", lhs, rhs) + + + def build_and(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = and , """ + return self._build_binop("and", lhs, rhs) + + + def build_or(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = or , """ + return self._build_binop("or", lhs, rhs) + + + def build_xor(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = xor , """ + return self._build_binop("xor", lhs, rhs) + + + def build_shl(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = shl , """ + return self._build_binop("shl", lhs, rhs) + + + def build_lshr(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = lshr , """ + return self._build_binop("lshr", lhs, rhs) + + + def build_ashr(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = ashr , """ + return self._build_binop("ashr", lhs, rhs) + + + # ============================================================ + # 浮点二元运算指令 + # ============================================================ + def build_fadd(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = fadd , """ + return self._build_binop("fadd", lhs, rhs) + + + def build_fsub(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = fsub , """ + return self._build_binop("fsub", lhs, rhs) + + + def build_fmul(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = fmul , """ + return self._build_binop("fmul", lhs, rhs) + + + def build_fdiv(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = fdiv , """ + return self._build_binop("fdiv", lhs, rhs) + + + def build_frem(self, lhs: Value | t.CPtr, + rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = frem , """ + return self._build_binop("frem", lhs, rhs) + + + def build_fneg(self, val: Value | t.CPtr) -> Value | t.CPtr: + """%N = fneg """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + line: t.CChar | t.CPtr = pool.alloc(128) + if line is None: return None + viperlib.snprintf(line, 128, "%s = fneg %s %s", name, ty_s, val.Name) + self._emit(line) + return SSAValue(pool, val.Ty, name) + + + # ============================================================ + # 比较指令 + # ============================================================ + def build_icmp(self, pred: t.CInt, + lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = icmp , + + 返回类型为 i1 的 SSA 值 + """ + if self is None or lhs is None or rhs is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(lhs.Ty) + pred_s: t.CChar | t.CPtr = _icmp_pred_str(pred) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = icmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) + self._emit(line) + + # 结果类型是 i1 + i1_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) + if i1_ty is not None: + string.memset(i1_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(i1_ty, LLVMType.Int(1)) + return SSAValue(pool, i1_ty, name) + + + def build_fcmp(self, pred: t.CInt, + lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: + """%N = fcmp , + + 返回类型为 i1 的 SSA 值 + """ + if self is None or lhs is None or rhs is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(lhs.Ty) + pred_s: t.CChar | t.CPtr = _fcmp_pred_str(pred) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fcmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) + self._emit(line) + + # 结果类型是 i1 + i1_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) + if i1_ty is not None: + string.memset(i1_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(i1_ty, LLVMType.Int(1)) + return SSAValue(pool, i1_ty, name) + + + # ============================================================ + # 终止指令 + # ============================================================ + def build_br(self, target: BasicBlock | t.CPtr): + """br label %""" + if self is None or target is None: return + pool: memhub.MemBuddy | t.CPtr = self.Pool + line: t.CChar | t.CPtr = pool.alloc(64) + if line is None: return + viperlib.snprintf(line, 64, "br label %%%s", target.Name) + self._emit(line) + self._mark_terminated() + + + def build_cond_br(self, cond: Value | t.CPtr, + then_blk: BasicBlock | t.CPtr, else_blk: BasicBlock | t.CPtr): + """br i1 , label %, label %""" + if self is None or cond is None or then_blk is None or else_blk is None: return + pool: memhub.MemBuddy | t.CPtr = self.Pool + line: t.CChar | t.CPtr = pool.alloc(128) + if line is None: return + viperlib.snprintf(line, 128, "br i1 %s, label %%%s, label %%%s", + cond.Name, then_blk.Name, else_blk.Name) + self._emit(line) + self._mark_terminated() + + + def build_ret(self, val: Value | t.CPtr): + """ret """ + if self is None or val is None: return + pool: memhub.MemBuddy | t.CPtr = self.Pool + ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + line: t.CChar | t.CPtr = pool.alloc(128) + if line is None: return + viperlib.snprintf(line, 128, "ret %s %s", ty_s, val.Name) + self._emit(line) + self._mark_terminated() + + + def build_ret_void(self): + """ret void""" + if self is None: return + pool: memhub.MemBuddy | t.CPtr = self.Pool + line: t.CChar | t.CPtr = pool.alloc(16) + if line is None: return + string.strcpy(line, "ret void") + self._emit(line) + self._mark_terminated() + + + # ============================================================ + # 调用指令 + # ============================================================ + def build_call(self, callee: t.CChar | t.CPtr, + args: Value | t.CPtr, arg_count: t.CInt, + ret_ty: LLVMType | t.CPtr, + is_variadic: t.CInt = 0) -> Value | t.CPtr: + """%N = call @() + + args 是 Value 链表头,arg_count 是参数数量。 + 若 ret_ty 是 void,不分配 SSA 名(使用 call 而非 %N = call)。 + is_variadic=1 时发射函数类型签名(如 i32 (i8*, ...) @printf)。 + """ + if self is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + + # 判断返回类型是否为 void + is_void: t.CInt = 0 + match ret_ty: + case LLVMType.Void(): + is_void = 1 + case _: + is_void = 0 + + name: t.CChar | t.CPtr = None + if is_void == 0: + name = self._alloc_ssa_name() + + ret_ty_s: t.CChar | t.CPtr = self._type_str(ret_ty) + + # 构建参数列表文本 + 变参函数类型签名 + 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(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, 1024, ", ") + _append_cstr(func_ty_buf, 1024, ", ") + arg_ty_s: t.CChar | t.CPtr = self._type_str(cur.Ty) + _append_cstr(args_buf, 1024, arg_ty_s) + _append_cstr(args_buf, 1024, " ") + if cur.Name is not None: + _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, 1024, ", ") + _append_cstr(func_ty_buf, 1024, "...") + + # 构造带引号的 callee 名字(SHA1 前缀名含 '.' 或以数字开头需要加引号) + callee_buf: t.CChar | t.CPtr = pool.alloc(128) + if callee_buf is None: return None + callee_buf[0] = '\0' + if _ll_name_needs_quote(callee) != 0: + _append_cstr(callee_buf, 128, "\"") + _append_cstr(callee_buf, 128, callee) + _append_cstr(callee_buf, 128, "\"") + else: + _append_cstr(callee_buf, 128, callee) + + 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, 2048, "call %s (%s) @%s(%s)", ret_ty_s, func_ty_buf, callee_buf, args_buf) + else: + 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, 2048, "call %s @%s(%s)", ret_ty_s, callee_buf, args_buf) + else: + viperlib.snprintf(line, 2048, "%s = call %s @%s(%s)", name, ret_ty_s, callee_buf, args_buf) + self._emit(line) + + if is_void == 1: + return None + return SSAValue(pool, ret_ty, name) + + + # ============================================================ + # 间接调用指令: call %callee() + # + # callee_val 是函数指针 SSA 值(必须已是函数指针类型,如 i32(i8*)*) + # ============================================================ + def build_call_indirect(self, callee_val: Value | t.CPtr, + args: Value | t.CPtr, arg_count: t.CInt, + ret_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = call %callee() + + 间接调用:callee_val 是函数指针 SSA 值。 + """ + if self is None or callee_val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + + is_void: t.CInt = 0 + match ret_ty: + case LLVMType.Void(): + is_void = 1 + case _: + is_void = 0 + + name: t.CChar | t.CPtr = None + if is_void == 0: + name = self._alloc_ssa_name() + + ret_ty_s: t.CChar | t.CPtr = self._type_str(ret_ty) + + # 构建参数列表文本 + 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, 1024, ", ") + arg_ty_s: t.CChar | t.CPtr = self._type_str(cur.Ty) + _append_cstr(args_buf, 1024, arg_ty_s) + _append_cstr(args_buf, 1024, " ") + if cur.Name is not None: + _append_cstr(args_buf, 1024, cur.Name) + cur = cur.Next + first = 0 + + line: t.CChar | t.CPtr = pool.alloc(2048) + if line is None: return None + if is_void == 1: + viperlib.snprintf(line, 2048, "call %s %s(%s)", ret_ty_s, callee_val.Name, args_buf) + else: + viperlib.snprintf(line, 2048, "%s = call %s %s(%s)", name, ret_ty_s, callee_val.Name, args_buf) + self._emit(line) + + if is_void == 1: + return None + return SSAValue(pool, ret_ty, name) + + + # ============================================================ + # 类型转换指令 + # ============================================================ + def build_bitcast(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = bitcast to """ + if self is None or val is None: + return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + if val.Name is None: + return None + viperlib.snprintf(line, 256, "%s = bitcast %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + ret: Value | t.CPtr = SSAValue(pool, to_ty, name) + return ret + + + def build_sext(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = sext to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = sext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_trunc(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = trunc to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = trunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_zext(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = zext to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = zext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_ptrtoint(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = ptrtoint to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = ptrtoint %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_inttoptr(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = inttoptr to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = inttoptr %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + # ============================================================ + # 浮点转换指令 + # ============================================================ + def build_fpext(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = fpext to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fpext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_fptrunc(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = fptrunc to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fptrunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_fp2si(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = fptosi to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fptosi %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_si2fp(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = sitofp to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = sitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_fp2ui(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = fptoui to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fptoui %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_ui2fp(self, val: Value | t.CPtr, + to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = uitofp to """ + if self is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + from_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + to_ty_s: t.CChar | t.CPtr = self._type_str(to_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = uitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + # ============================================================ + # GEP 指令 + # ============================================================ + def build_gep(self, elem_ty: LLVMType | t.CPtr, + ptr: Value | t.CPtr, idx: Value | t.CPtr) -> Value | t.CPtr: + """%N = getelementptr , , """ + if self is None or ptr is None or idx is None: return None + # 防御性检查: ptr.Ty 必须是指针类型,否则 llc 报错 + # "base of getelementptr must be a pointer" + # 当 translate_value 返回非指针值(如 i32 常量 0)时, + # 直接 GEP 会导致 llc 编译失败 + if _is_ptr_type(ptr.Ty) == 0: + stdio.printf("[GEP-ERR] base not pointer, skip GEP\n") + return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + elem_ty_s: t.CChar | t.CPtr = self._type_str(elem_ty) + ptr_ty_s: t.CChar | t.CPtr = self._type_str(ptr.Ty) + idx_ty_s: t.CChar | t.CPtr = self._type_str(idx.Ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, %s %s", + name, elem_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) + self._emit(line) + + # GEP 结果类型是 elem_ty* + result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) + if result_ty is not None: + string.memset(result_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty, LLVMType.Ptr(elem_ty)) + return SSAValue(pool, result_ty, name) + + + # ============================================================ + # build_gep_array — 数组双索引 GEP: getelementptr [N x ty], [N x ty]* %ptr, i32 0, i32 %idx + # ============================================================ + def build_gep_array(self, array_ty: LLVMType | t.CPtr, + elem_ty: LLVMType | t.CPtr, + ptr: Value | t.CPtr, idx: Value | t.CPtr) -> Value | t.CPtr: + """%N = getelementptr [N x elem_ty], [N x elem_ty]* %ptr, i32 0, i32 %idx + + 结果类型是 elem_ty* + """ + if self is None or ptr is None or idx is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + array_ty_s: t.CChar | t.CPtr = self._type_str(array_ty) + ptr_ty_s: t.CChar | t.CPtr = self._type_str(ptr.Ty) + idx_ty_s: t.CChar | t.CPtr = self._type_str(idx.Ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, i32 0, %s %s", + name, array_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) + self._emit(line) + + # GEP 结果类型是 elem_ty* + result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) + if result_ty is not None: + string.memset(result_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty, LLVMType.Ptr(elem_ty)) + return SSAValue(pool, result_ty, name) + + + # ============================================================ + # build_gep_struct — 结构体字段 GEP: getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx + # ============================================================ + def build_gep_struct(self, struct_ty: LLVMType | t.CPtr, + field_ty: LLVMType | t.CPtr, + ptr: Value | t.CPtr, field_idx: t.CInt) -> Value | t.CPtr: + """%N = getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx + + 结果类型是 field_ty* + """ + if self is None or ptr is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + struct_ty_s: t.CChar | t.CPtr = self._type_str(struct_ty) + ptr_ty_s: t.CChar | t.CPtr = self._type_str(ptr.Ty) + line: t.CChar | t.CPtr = pool.alloc(2048) + if line is None: + stdio.printf("[BGS] line alloc None\n") + 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) + self._emit(line) + + # GEP 结果类型是 field_ty* + result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) + if result_ty is None: + stdio.printf("[BGS] result_ty alloc None\n") + else: + string.memset(result_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty, LLVMType.Ptr(field_ty)) + rv: Value | t.CPtr = SSAValue(pool, result_ty, name) + if rv is None: + stdio.printf("[BGS] SSAValue None\n") + return rv + + + # ============================================================ + # Phi 指令 + # ============================================================ + def build_phi(self, ty: LLVMType | t.CPtr, + incoming_head: PhiIncoming | t.CPtr, count: t.CInt) -> Value | t.CPtr: + """%N = phi [val1, %blk1], [val2, %blk2], ... + + incoming_head 是 PhiIncoming 链表头,count 是入边数量。 + """ + if self is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(ty) + + # 构建入边列表文本: [val, %blk], [val, %blk], ... + # 入边可能很多(如 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, 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, 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, 4096, inc_blk_name) + _append_cstr(parts, 4096, "]") + cur = cur.Next + first = 0 + + line: t.CChar | t.CPtr = pool.alloc(8192) + if line is None: return None + viperlib.snprintf(line, 8192, "%s = phi %s %s", name, ty_s, parts) + self._emit(line) + return SSAValue(pool, ty, name) + + + # ============================================================ + # Switch 指令 + # ============================================================ + def build_switch(self, val: Value | t.CPtr, + default_blk: BasicBlock | t.CPtr, + cases_head: SwitchCase | t.CPtr, case_count: t.CInt): + """switch , label % [, label % ...]""" + if self is None or val is None or default_blk is None: return + pool: memhub.MemBuddy | t.CPtr = self.Pool + ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + + # 构建主体: switch , label %default + line: t.CChar | t.CPtr = pool.alloc(512) + if line is None: return + viperlib.snprintf(line, 512, "switch %s %s, label %%%s", + ty_s, val.Name, default_blk.Name) + _append_cstr(line, 512, " [") + _append_cstr(line, 512, "\n") + + # 遍历 case 列表 + cur: SwitchCase | t.CPtr = cases_head + case_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + while cur is not None: + case_line: t.CChar | t.CPtr = pool.alloc(128) + if case_line is not None: + # 使用中间变量避免链式结构体成员访问被编译器丢弃 + cv: Value | t.CPtr = cur.CaseVal + cb: BasicBlock | t.CPtr = cur.Block + if cv is not None and cb is not None: + cv_name: t.CChar | t.CPtr = cv.Name + cb_name: t.CChar | t.CPtr = cb.Name + if cv_name is not None and cb_name is not None: + viperlib.snprintf(case_line, 128, " %s %s, label %%%s\n", + case_ty_s, cv_name, cb_name) + _append_cstr(line, 512, case_line) + cur = cur.Next + + _append_cstr(line, 512, "]") + self._emit(line) + self._mark_terminated() + + + # ============================================================ + # Select 指令 + # ============================================================ + def build_select(self, cond: Value | t.CPtr, + true_val: Value | t.CPtr, false_val: Value | t.CPtr) -> Value | t.CPtr: + """%N = select i1 , , """ + if self is None or cond is None or true_val is None or false_val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ty_s: t.CChar | t.CPtr = self._type_str(true_val.Ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = select i1 %s, %s %s, %s %s", + name, cond.Name, ty_s, true_val.Name, ty_s, false_val.Name) + self._emit(line) + return SSAValue(pool, true_val.Ty, name) + + + # ============================================================ + # Unreachable 指令 + # ============================================================ + def build_unreachable(self): + """unreachable""" + if self is None: return + pool: memhub.MemBuddy | t.CPtr = self.Pool + line: t.CChar | t.CPtr = pool.alloc(16) + if line is None: return + string.strcpy(line, "unreachable") + self._emit(line) + self._mark_terminated() + + + # ============================================================ + # 原子指令 (Phase 3) + # ============================================================ + def build_atomicrmw(self, op: t.CInt, + ptr: Value | t.CPtr, val: Value | t.CPtr, + order: t.CInt) -> Value | t.CPtr: + """%N = atomicrmw , """ + if self is None or ptr is None or val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ptr_ty_s: t.CChar | t.CPtr = self._type_str(ptr.Ty) + val_ty_s: t.CChar | t.CPtr = self._type_str(val.Ty) + op_s: t.CChar | t.CPtr = _atomic_op_str(op) + ord_s: t.CChar | t.CPtr = _atomic_order_str(order) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = atomicrmw %s %s %s, %s %s %s", + name, op_s, ptr_ty_s, ptr.Name, val_ty_s, val.Name, ord_s) + self._emit(line) + return SSAValue(pool, val.Ty, name) + + + def build_cmpxchg(self, ptr: Value | t.CPtr, + cmp_val: Value | t.CPtr, new_val: Value | t.CPtr, + succ_order: t.CInt, fail_order: t.CInt) -> Value | t.CPtr: + """%N = cmpxchg , , + + 返回 {, i1} 类型的 SSA 值(简化为返回 i64 表示的值对) + """ + if self is None or ptr is None or cmp_val is None or new_val is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + ptr_ty_s: t.CChar | t.CPtr = self._type_str(ptr.Ty) + cmp_ty_s: t.CChar | t.CPtr = self._type_str(cmp_val.Ty) + new_ty_s: t.CChar | t.CPtr = self._type_str(new_val.Ty) + succ_s: t.CChar | t.CPtr = _atomic_order_str(succ_order) + fail_s: t.CChar | t.CPtr = _atomic_order_str(fail_order) + line: t.CChar | t.CPtr = pool.alloc(384) + if line is None: return None + viperlib.snprintf(line, 384, "%s = cmpxchg %s %s, %s %s, %s %s %s %s", + name, ptr_ty_s, ptr.Name, cmp_ty_s, cmp_val.Name, + new_ty_s, new_val.Name, succ_s, fail_s) + self._emit(line) + # 返回原始值的类型 + return SSAValue(pool, new_val.Ty, name) + + + # ============================================================ + # va_arg 指令: 从 va_list 读取下一个变长参数 + # ============================================================ + def build_va_arg(self, + va_list_ptr: Value | t.CPtr, + arg_ty: LLVMType | t.CPtr) -> Value | t.CPtr: + """%N = va_arg , """ + if self is None or va_list_ptr is None or arg_ty is None: + return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + name: t.CChar | t.CPtr = self._alloc_ssa_name() + va_list_ty_s: t.CChar | t.CPtr = self._type_str(va_list_ptr.Ty) + arg_ty_s: t.CChar | t.CPtr = self._type_str(arg_ty) + line: t.CChar | t.CPtr = pool.alloc(256) + if line is None: return None + if va_list_ptr.Name is None: + return None + viperlib.snprintf(line, 256, "%s = va_arg %s %s, %s", + name, va_list_ty_s, va_list_ptr.Name, arg_ty_s) + self._emit(line) + return SSAValue(pool, arg_ty, name) + + + # ============================================================ + # 内联汇编 (Phase 3) + # ============================================================ + def build_inline_asm(self, ret_ty: LLVMType | t.CPtr, + asm_text: t.CChar | t.CPtr, constraints: t.CChar | t.CPtr, + has_side_effects: t.CInt) -> Value | t.CPtr: + """%N = call asm [sideeffect] "" ""() + + 简化的内联汇编调用,无参数版本。 + """ + if self is None: return None + pool: memhub.MemBuddy | t.CPtr = self.Pool + + # 判断返回类型是否为 void + is_void: t.CInt = 0 + match ret_ty: + case LLVMType.Void(): + is_void = 1 + case _: + is_void = 0 + + name: t.CChar | t.CPtr = None + if is_void == 0: + name = self._alloc_ssa_name() + + ret_ty_s: t.CChar | t.CPtr = self._type_str(ret_ty) + line: t.CChar | t.CPtr = pool.alloc(512) + if line is None: return None + + if has_side_effects != 0: + if is_void == 1: + viperlib.snprintf(line, 512, "call %s asm sideeffect \"%s\", \"%s\"()", + ret_ty_s, asm_text, constraints) + else: + viperlib.snprintf(line, 512, "%s = call %s asm sideeffect \"%s\", \"%s\"()", + name, ret_ty_s, asm_text, constraints) + else: + if is_void == 1: + viperlib.snprintf(line, 512, "call %s asm \"%s\", \"%s\"()", + ret_ty_s, asm_text, constraints) + else: + viperlib.snprintf(line, 512, "%s = call %s asm \"%s\", \"%s\"()", + name, ret_ty_s, asm_text, constraints) + self._emit(line) + + if is_void == 1: + return None + return SSAValue(pool, ret_ty, name) + # ============================================================ # 工厂函数 @@ -62,10 +1154,7 @@ def position_at_end(builder: IRBuilder | t.CPtr, block: BasicBlock | t.CPtr): 移动 block 是为了保证 BB 文本输出顺序与控制流(position)顺序一致, 避免 SSA 名在文本中非单调递增导致 llc 编译失败。 """ - if builder is None: return - builder.CurBlock = block - if builder.Func is not None: - function_move_block_to_end(builder.Func, block) + builder.position_at_end(block) # ============================================================ @@ -73,12 +1162,7 @@ def position_at_end(builder: IRBuilder | t.CPtr, block: BasicBlock | t.CPtr): # ============================================================ def _alloc_ssa_name(builder: IRBuilder | t.CPtr) -> t.CChar | t.CPtr: """分配一个 SSA 名字符串 "%N" 并递增计数器""" - pool: memhub.MemBuddy | t.CPtr = builder.Pool - buf: t.CChar | t.CPtr = pool.alloc(16) - if buf is None: return None - viperlib.snprintf(buf, 16, "%%%d", builder.Counter) - builder.Counter += 1 - return buf + return builder._alloc_ssa_name() def _type_str(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> t.CChar | t.CPtr: @@ -87,24 +1171,17 @@ def _type_str(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> t.CChar | t 注意:复杂结构体类型(如 13 方法的虚表 {i8*,i8*,...})可能超过 64 字节, 必须使用足够大的缓冲区,否则 '}' 会被截断导致 IR 语法错误。 """ - pool: memhub.MemBuddy | t.CPtr = builder.Pool - buf: t.CChar | t.CPtr = pool.alloc(512) - if buf is None: return None - buf[0] = '\0' - TypePrint(buf, 512, ty, pool) - return buf + return builder._type_str(ty) def _emit(builder: IRBuilder | t.CPtr, text: t.CChar | t.CPtr): """将指令文本追加到当前块""" - if builder is None or builder.CurBlock is None: return - block_append_text(builder.Pool, builder.CurBlock, text) + builder._emit(text) def _mark_terminated(builder: IRBuilder | t.CPtr): """标记当前块已终止""" - if builder is None or builder.CurBlock is None: return - block_set_terminated(builder.CurBlock, 1) + builder._mark_terminated() def builder_cur_block_is_terminated(builder: IRBuilder | t.CPtr) -> t.CInt: @@ -114,9 +1191,7 @@ def builder_cur_block_is_terminated(builder: IRBuilder | t.CPtr) -> t.CInt: builder.CurBlock.IsTerminated 在跨模块场景下 BasicBlock 为 opaque 导致的 GEP 静默跳过问题。 """ - if builder is None or builder.CurBlock is None: - return 1 - return block_get_terminated(builder.CurBlock) + return builder.builder_cur_block_is_terminated() # ============================================================ @@ -129,22 +1204,7 @@ def build_alloca(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> Value | 注意:TPC 并行编译时 include 模块的函数默认参数不会预注册(argN 名回退), 因此本函数不设默认参数;需要显式对齐请用 build_alloca_aligned。 """ - if builder is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, ty) - line: t.CChar | t.CPtr = pool.alloc(128) - if line is None: return None - viperlib.snprintf(line, 128, "%s = alloca %s", name, ty_s) - _emit(builder, line) - - # 结果类型是 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) + return builder.build_alloca(ty) def build_alloca_aligned(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr, @@ -154,22 +1214,7 @@ def build_alloca_aligned(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr, 返回类型为 ty* 的 SSA 值;显式指定对齐(如 va_list 24 字节缓冲区需 16 对齐)。 align 不设默认值(TPC 并行编译时 include 默认参数不预注册),调用方必须显式传入。 """ - if builder is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, ty) - line: t.CChar | t.CPtr = pool.alloc(128) - if line is None: return None - viperlib.snprintf(line, 128, "%s = alloca %s, align %d", name, ty_s, align) - _emit(builder, line) - - # 结果类型是 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) + return builder.build_alloca_aligned(ty, align) def build_alloca_at_entry(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> Value | t.CPtr: @@ -178,55 +1223,18 @@ def build_alloca_at_entry(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr) -> 在入口块的终止指令之前插入 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) + return builder.build_alloca_at_entry(ty) def build_load(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr, ptr: Value | t.CPtr) -> Value | t.CPtr: """%N = load , """ - if builder is None or ptr is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, ty) - ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = load %s, %s %s", name, ty_s, ptr_ty_s, ptr.Name) - _emit(builder, line) - return SSAValue(pool, ty, name) + return builder.build_load(ty, ptr) def build_store(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, ptr: Value | t.CPtr): """store , """ - if builder is None or val is None or ptr is None: return - pool: memhub.MemBuddy | t.CPtr = builder.Pool - val_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return - viperlib.snprintf(line, 256, "store %s %s, %s %s", val_ty_s, val.Name, ptr_ty_s, ptr.Name) - _emit(builder, line) + builder.build_store(val, ptr) # ============================================================ @@ -235,79 +1243,79 @@ def build_store(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, ptr: Value | t def build_add(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = add , """ - return _build_binop(builder, "add", lhs, rhs) + return builder.build_add(lhs, rhs) def build_sub(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = sub , """ - return _build_binop(builder, "sub", lhs, rhs) + return builder.build_sub(lhs, rhs) def build_mul(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = mul , """ - return _build_binop(builder, "mul", lhs, rhs) + return builder.build_mul(lhs, rhs) def build_sdiv(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = sdiv , """ - return _build_binop(builder, "sdiv", lhs, rhs) + return builder.build_sdiv(lhs, rhs) def build_udiv(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = udiv , """ - return _build_binop(builder, "udiv", lhs, rhs) + return builder.build_udiv(lhs, rhs) def build_srem(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = srem , """ - return _build_binop(builder, "srem", lhs, rhs) + return builder.build_srem(lhs, rhs) def build_urem(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = urem , """ - return _build_binop(builder, "urem", lhs, rhs) + return builder.build_urem(lhs, rhs) def build_and(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = and , """ - return _build_binop(builder, "and", lhs, rhs) + return builder.build_and(lhs, rhs) def build_or(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = or , """ - return _build_binop(builder, "or", lhs, rhs) + return builder.build_or(lhs, rhs) def build_xor(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = xor , """ - return _build_binop(builder, "xor", lhs, rhs) + return builder.build_xor(lhs, rhs) def build_shl(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = shl , """ - return _build_binop(builder, "shl", lhs, rhs) + return builder.build_shl(lhs, rhs) def build_lshr(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = lshr , """ - return _build_binop(builder, "lshr", lhs, rhs) + return builder.build_lshr(lhs, rhs) def build_ashr(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = ashr , """ - return _build_binop(builder, "ashr", lhs, rhs) + return builder.build_ashr(lhs, rhs) # ============================================================ @@ -316,58 +1324,42 @@ def build_ashr(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, def build_fadd(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = fadd , """ - return _build_binop(builder, "fadd", lhs, rhs) + return builder.build_fadd(lhs, rhs) def build_fsub(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = fsub , """ - return _build_binop(builder, "fsub", lhs, rhs) + return builder.build_fsub(lhs, rhs) def build_fmul(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = fmul , """ - return _build_binop(builder, "fmul", lhs, rhs) + return builder.build_fmul(lhs, rhs) def build_fdiv(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = fdiv , """ - return _build_binop(builder, "fdiv", lhs, rhs) + return builder.build_fdiv(lhs, rhs) def build_frem(builder: IRBuilder | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """%N = frem , """ - return _build_binop(builder, "frem", lhs, rhs) + return builder.build_frem(lhs, rhs) def build_fneg(builder: IRBuilder | t.CPtr, val: Value | t.CPtr) -> Value | t.CPtr: """%N = fneg """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - line: t.CChar | t.CPtr = pool.alloc(128) - if line is None: return None - viperlib.snprintf(line, 128, "%s = fneg %s %s", name, ty_s, val.Name) - _emit(builder, line) - return SSAValue(pool, val.Ty, name) + return builder.build_fneg(val) def _build_binop(builder: IRBuilder | t.CPtr, op: t.CChar | t.CPtr, lhs: Value | t.CPtr, rhs: Value | t.CPtr) -> Value | t.CPtr: """二元运算通用实现""" - if builder is None or lhs is None or rhs is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, lhs.Ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = %s %s %s, %s", name, op, ty_s, lhs.Name, rhs.Name) - _emit(builder, line) - return SSAValue(pool, lhs.Ty, name) + return builder._build_binop(op, lhs, rhs) # ============================================================ @@ -444,22 +1436,7 @@ def build_icmp(builder: IRBuilder | t.CPtr, pred: t.CInt, 返回类型为 i1 的 SSA 值 """ - if builder is None or lhs is None or rhs is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, lhs.Ty) - pred_s: t.CChar | t.CPtr = _icmp_pred_str(pred) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = icmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) - _emit(builder, line) - - # 结果类型是 i1 - i1_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) - if i1_ty is not None: - string.memset(i1_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(i1_ty, LLVMType.Int(1)) - return SSAValue(pool, i1_ty, name) + return builder.build_icmp(pred, lhs, rhs) def build_fcmp(builder: IRBuilder | t.CPtr, pred: t.CInt, @@ -468,22 +1445,7 @@ def build_fcmp(builder: IRBuilder | t.CPtr, pred: t.CInt, 返回类型为 i1 的 SSA 值 """ - if builder is None or lhs is None or rhs is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, lhs.Ty) - pred_s: t.CChar | t.CPtr = _fcmp_pred_str(pred) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fcmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) - _emit(builder, line) - - # 结果类型是 i1 - i1_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) - if i1_ty is not None: - string.memset(i1_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(i1_ty, LLVMType.Int(1)) - return SSAValue(pool, i1_ty, name) + return builder.build_fcmp(pred, lhs, rhs) # ============================================================ @@ -491,49 +1453,23 @@ def build_fcmp(builder: IRBuilder | t.CPtr, pred: t.CInt, # ============================================================ def build_br(builder: IRBuilder | t.CPtr, target: BasicBlock | t.CPtr): """br label %""" - if builder is None or target is None: return - pool: memhub.MemBuddy | t.CPtr = builder.Pool - line: t.CChar | t.CPtr = pool.alloc(64) - if line is None: return - viperlib.snprintf(line, 64, "br label %%%s", target.Name) - _emit(builder, line) - _mark_terminated(builder) + builder.build_br(target) def build_cond_br(builder: IRBuilder | t.CPtr, cond: Value | t.CPtr, then_blk: BasicBlock | t.CPtr, else_blk: BasicBlock | t.CPtr): """br i1 , label %, label %""" - if builder is None or cond is None or then_blk is None or else_blk is None: return - pool: memhub.MemBuddy | t.CPtr = builder.Pool - line: t.CChar | t.CPtr = pool.alloc(128) - if line is None: return - viperlib.snprintf(line, 128, "br i1 %s, label %%%s, label %%%s", - cond.Name, then_blk.Name, else_blk.Name) - _emit(builder, line) - _mark_terminated(builder) + builder.build_cond_br(cond, then_blk, else_blk) def build_ret(builder: IRBuilder | t.CPtr, val: Value | t.CPtr): """ret """ - if builder is None or val is None: return - pool: memhub.MemBuddy | t.CPtr = builder.Pool - ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - line: t.CChar | t.CPtr = pool.alloc(128) - if line is None: return - viperlib.snprintf(line, 128, "ret %s %s", ty_s, val.Name) - _emit(builder, line) - _mark_terminated(builder) + builder.build_ret(val) def build_ret_void(builder: IRBuilder | t.CPtr): """ret void""" - if builder is None: return - pool: memhub.MemBuddy | t.CPtr = builder.Pool - line: t.CChar | t.CPtr = pool.alloc(16) - if line is None: return - string.strcpy(line, "ret void") - _emit(builder, line) - _mark_terminated(builder) + builder.build_ret_void() # ============================================================ @@ -549,77 +1485,7 @@ def build_call(builder: IRBuilder | t.CPtr, callee: t.CChar | t.CPtr, 若 ret_ty 是 void,不分配 SSA 名(使用 call 而非 %N = call)。 is_variadic=1 时发射函数类型签名(如 i32 (i8*, ...) @printf)。 """ - if builder is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - - # 判断返回类型是否为 void - is_void: t.CInt = 0 - match ret_ty: - case LLVMType.Void(): - is_void = 1 - case _: - is_void = 0 - - name: t.CChar | t.CPtr = None - if is_void == 0: - name = _alloc_ssa_name(builder) - - ret_ty_s: t.CChar | t.CPtr = _type_str(builder, ret_ty) - - # 构建参数列表文本 + 变参函数类型签名 - 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(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, 1024, ", ") - _append_cstr(func_ty_buf, 1024, ", ") - arg_ty_s: t.CChar | t.CPtr = _type_str(builder, cur.Ty) - _append_cstr(args_buf, 1024, arg_ty_s) - _append_cstr(args_buf, 1024, " ") - if cur.Name is not None: - _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, 1024, ", ") - _append_cstr(func_ty_buf, 1024, "...") - - # 构造带引号的 callee 名字(SHA1 前缀名含 '.' 或以数字开头需要加引号) - callee_buf: t.CChar | t.CPtr = pool.alloc(128) - if callee_buf is None: return None - callee_buf[0] = '\0' - if _ll_name_needs_quote(callee) != 0: - _append_cstr(callee_buf, 128, "\"") - _append_cstr(callee_buf, 128, callee) - _append_cstr(callee_buf, 128, "\"") - else: - _append_cstr(callee_buf, 128, callee) - - 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, 2048, "call %s (%s) @%s(%s)", ret_ty_s, func_ty_buf, callee_buf, args_buf) - else: - 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, 2048, "call %s @%s(%s)", ret_ty_s, callee_buf, args_buf) - else: - viperlib.snprintf(line, 2048, "%s = call %s @%s(%s)", name, ret_ty_s, callee_buf, args_buf) - _emit(builder, line) - - if is_void == 1: - return None - return SSAValue(pool, ret_ty, name) + return builder.build_call(callee, args, arg_count, ret_ty, is_variadic) # ============================================================ @@ -634,50 +1500,7 @@ def build_call_indirect(builder: IRBuilder | t.CPtr, callee_val: Value | t.CPtr, 间接调用:callee_val 是函数指针 SSA 值。 """ - if builder is None or callee_val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - - is_void: t.CInt = 0 - match ret_ty: - case LLVMType.Void(): - is_void = 1 - case _: - is_void = 0 - - name: t.CChar | t.CPtr = None - if is_void == 0: - name = _alloc_ssa_name(builder) - - ret_ty_s: t.CChar | t.CPtr = _type_str(builder, ret_ty) - - # 构建参数列表文本 - 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, 1024, ", ") - arg_ty_s: t.CChar | t.CPtr = _type_str(builder, cur.Ty) - _append_cstr(args_buf, 1024, arg_ty_s) - _append_cstr(args_buf, 1024, " ") - if cur.Name is not None: - _append_cstr(args_buf, 1024, cur.Name) - cur = cur.Next - first = 0 - - line: t.CChar | t.CPtr = pool.alloc(2048) - if line is None: return None - if is_void == 1: - viperlib.snprintf(line, 2048, "call %s %s(%s)", ret_ty_s, callee_val.Name, args_buf) - else: - 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: - return None - return SSAValue(pool, ret_ty, name) + return builder.build_call_indirect(callee_val, args, arg_count, ret_ty) # ============================================================ @@ -686,95 +1509,37 @@ def build_call_indirect(builder: IRBuilder | t.CPtr, callee_val: Value | t.CPtr, def build_bitcast(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = bitcast to """ - if builder is None or val is None: - return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - if val.Name is None: - return None - viperlib.snprintf(line, 256, "%s = bitcast %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - ret: Value | t.CPtr = SSAValue(pool, to_ty, name) - return ret + return builder.build_bitcast(val, to_ty) def build_sext(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = sext to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = sext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_sext(val, to_ty) def build_trunc(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = trunc to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = trunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_trunc(val, to_ty) def build_zext(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = zext to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = zext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_zext(val, to_ty) def build_ptrtoint(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = ptrtoint to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = ptrtoint %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_ptrtoint(val, to_ty) def build_inttoptr(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = inttoptr to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = inttoptr %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_inttoptr(val, to_ty) # ============================================================ @@ -783,91 +1548,37 @@ def build_inttoptr(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, def build_fpext(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = fpext to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fpext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_fpext(val, to_ty) def build_fptrunc(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = fptrunc to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fptrunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_fptrunc(val, to_ty) def build_fp2si(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = fptosi to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fptosi %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_fp2si(val, to_ty) def build_si2fp(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = sitofp to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = sitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_si2fp(val, to_ty) def build_fp2ui(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = fptoui to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fptoui %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_fp2ui(val, to_ty) def build_ui2fp(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, to_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = uitofp to """ - if builder is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - from_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - to_ty_s: t.CChar | t.CPtr = _type_str(builder, to_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = uitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - _emit(builder, line) - return SSAValue(pool, to_ty, name) + return builder.build_ui2fp(val, to_ty) # ============================================================ @@ -887,31 +1598,7 @@ def _is_ptr_type(ty: LLVMType | t.CPtr) -> int: def build_gep(builder: IRBuilder | t.CPtr, elem_ty: LLVMType | t.CPtr, ptr: Value | t.CPtr, idx: Value | t.CPtr) -> Value | t.CPtr: """%N = getelementptr , , """ - if builder is None or ptr is None or idx is None: return None - # 防御性检查: ptr.Ty 必须是指针类型,否则 llc 报错 - # "base of getelementptr must be a pointer" - # 当 translate_value 返回非指针值(如 i32 常量 0)时, - # 直接 GEP 会导致 llc 编译失败 - if _is_ptr_type(ptr.Ty) == 0: - stdio.printf("[GEP-ERR] base not pointer, skip GEP\n") - return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - elem_ty_s: t.CChar | t.CPtr = _type_str(builder, elem_ty) - ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty) - idx_ty_s: t.CChar | t.CPtr = _type_str(builder, idx.Ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, %s %s", - name, elem_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) - _emit(builder, line) - - # GEP 结果类型是 elem_ty* - result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) - if result_ty is not None: - string.memset(result_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty, LLVMType.Ptr(elem_ty)) - return SSAValue(pool, result_ty, name) + return builder.build_gep(elem_ty, ptr, idx) # ============================================================ @@ -924,24 +1611,7 @@ def build_gep_array(builder: IRBuilder | t.CPtr, array_ty: LLVMType | t.CPtr, 结果类型是 elem_ty* """ - if builder is None or ptr is None or idx is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - array_ty_s: t.CChar | t.CPtr = _type_str(builder, array_ty) - ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty) - idx_ty_s: t.CChar | t.CPtr = _type_str(builder, idx.Ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, i32 0, %s %s", - name, array_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) - _emit(builder, line) - - # GEP 结果类型是 elem_ty* - result_ty: LLVMType | t.CPtr = pool.alloc(LLVMType.__sizeof__()) - if result_ty is not None: - string.memset(result_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty, LLVMType.Ptr(elem_ty)) - return SSAValue(pool, result_ty, name) + return builder.build_gep_array(array_ty, elem_ty, ptr, idx) # ============================================================ @@ -954,30 +1624,7 @@ def build_gep_struct(builder: IRBuilder | t.CPtr, struct_ty: LLVMType | t.CPtr, 结果类型是 field_ty* """ - if builder is None or ptr is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - 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: - stdio.printf("[BGS] line alloc None\n") - 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 None: - stdio.printf("[BGS] result_ty alloc None\n") - else: - string.memset(result_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty, LLVMType.Ptr(field_ty)) - rv: Value | t.CPtr = SSAValue(pool, result_ty, name) - if rv is None: - stdio.printf("[BGS] SSAValue None\n") - return rv + return builder.build_gep_struct(struct_ty, field_ty, ptr, field_idx) # ============================================================ @@ -1014,43 +1661,7 @@ def build_phi(builder: IRBuilder | t.CPtr, ty: LLVMType | t.CPtr, incoming_head 是 PhiIncoming 链表头,count 是入边数量。 """ - if builder is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, ty) - - # 构建入边列表文本: [val, %blk], [val, %blk], ... - # 入边可能很多(如 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, 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, 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, 4096, inc_blk_name) - _append_cstr(parts, 4096, "]") - cur = cur.Next - first = 0 - - line: t.CChar | t.CPtr = pool.alloc(8192) - if line is None: return None - viperlib.snprintf(line, 8192, "%s = phi %s %s", name, ty_s, parts) - _emit(builder, line) - return SSAValue(pool, ty, name) + return builder.build_phi(ty, incoming_head, count) # ============================================================ @@ -1084,39 +1695,7 @@ def build_switch(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, default_blk: BasicBlock | t.CPtr, cases_head: SwitchCase | t.CPtr, case_count: t.CInt): """switch , label % [, label % ...]""" - if builder is None or val is None or default_blk is None: return - pool: memhub.MemBuddy | t.CPtr = builder.Pool - ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - - # 构建主体: switch , label %default - line: t.CChar | t.CPtr = pool.alloc(512) - if line is None: return - viperlib.snprintf(line, 512, "switch %s %s, label %%%s", - ty_s, val.Name, default_blk.Name) - _append_cstr(line, 512, " [") - _append_cstr(line, 512, "\n") - - # 遍历 case 列表 - cur: SwitchCase | t.CPtr = cases_head - case_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - while cur is not None: - case_line: t.CChar | t.CPtr = pool.alloc(128) - if case_line is not None: - # 使用中间变量避免链式结构体成员访问被编译器丢弃 - cv: Value | t.CPtr = cur.CaseVal - cb: BasicBlock | t.CPtr = cur.Block - if cv is not None and cb is not None: - cv_name: t.CChar | t.CPtr = cv.Name - cb_name: t.CChar | t.CPtr = cb.Name - if cv_name is not None and cb_name is not None: - viperlib.snprintf(case_line, 128, " %s %s, label %%%s\n", - case_ty_s, cv_name, cb_name) - _append_cstr(line, 512, case_line) - cur = cur.Next - - _append_cstr(line, 512, "]") - _emit(builder, line) - _mark_terminated(builder) + builder.build_switch(val, default_blk, cases_head, case_count) # ============================================================ @@ -1125,16 +1704,7 @@ def build_switch(builder: IRBuilder | t.CPtr, val: Value | t.CPtr, def build_select(builder: IRBuilder | t.CPtr, cond: Value | t.CPtr, true_val: Value | t.CPtr, false_val: Value | t.CPtr) -> Value | t.CPtr: """%N = select i1 , , """ - if builder is None or cond is None or true_val is None or false_val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ty_s: t.CChar | t.CPtr = _type_str(builder, true_val.Ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = select i1 %s, %s %s, %s %s", - name, cond.Name, ty_s, true_val.Name, ty_s, false_val.Name) - _emit(builder, line) - return SSAValue(pool, true_val.Ty, name) + return builder.build_select(cond, true_val, false_val) # ============================================================ @@ -1142,13 +1712,7 @@ def build_select(builder: IRBuilder | t.CPtr, cond: Value | t.CPtr, # ============================================================ def build_unreachable(builder: IRBuilder | t.CPtr): """unreachable""" - if builder is None: return - pool: memhub.MemBuddy | t.CPtr = builder.Pool - line: t.CChar | t.CPtr = pool.alloc(16) - if line is None: return - string.strcpy(line, "unreachable") - _emit(builder, line) - _mark_terminated(builder) + builder.build_unreachable() # ============================================================ @@ -1206,19 +1770,7 @@ def build_atomicrmw(builder: IRBuilder | t.CPtr, op: t.CInt, ptr: Value | t.CPtr, val: Value | t.CPtr, order: t.CInt) -> Value | t.CPtr: """%N = atomicrmw , """ - if builder is None or ptr is None or val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty) - val_ty_s: t.CChar | t.CPtr = _type_str(builder, val.Ty) - op_s: t.CChar | t.CPtr = _atomic_op_str(op) - ord_s: t.CChar | t.CPtr = _atomic_order_str(order) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = atomicrmw %s %s %s, %s %s %s", - name, op_s, ptr_ty_s, ptr.Name, val_ty_s, val.Name, ord_s) - _emit(builder, line) - return SSAValue(pool, val.Ty, name) + return builder.build_atomicrmw(op, ptr, val, order) def build_cmpxchg(builder: IRBuilder | t.CPtr, ptr: Value | t.CPtr, @@ -1228,22 +1780,7 @@ def build_cmpxchg(builder: IRBuilder | t.CPtr, ptr: Value | t.CPtr, 返回 {, i1} 类型的 SSA 值(简化为返回 i64 表示的值对) """ - if builder is None or ptr is None or cmp_val is None or new_val is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - ptr_ty_s: t.CChar | t.CPtr = _type_str(builder, ptr.Ty) - cmp_ty_s: t.CChar | t.CPtr = _type_str(builder, cmp_val.Ty) - new_ty_s: t.CChar | t.CPtr = _type_str(builder, new_val.Ty) - succ_s: t.CChar | t.CPtr = _atomic_order_str(succ_order) - fail_s: t.CChar | t.CPtr = _atomic_order_str(fail_order) - line: t.CChar | t.CPtr = pool.alloc(384) - if line is None: return None - viperlib.snprintf(line, 384, "%s = cmpxchg %s %s, %s %s, %s %s %s %s", - name, ptr_ty_s, ptr.Name, cmp_ty_s, cmp_val.Name, - new_ty_s, new_val.Name, succ_s, fail_s) - _emit(builder, line) - # 返回原始值的类型 - return SSAValue(pool, new_val.Ty, name) + return builder.build_cmpxchg(ptr, cmp_val, new_val, succ_order, fail_order) # ============================================================ @@ -1253,20 +1790,7 @@ def build_va_arg(builder: IRBuilder | t.CPtr, va_list_ptr: Value | t.CPtr, arg_ty: LLVMType | t.CPtr) -> Value | t.CPtr: """%N = va_arg , """ - if builder is None or va_list_ptr is None or arg_ty is None: - return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - name: t.CChar | t.CPtr = _alloc_ssa_name(builder) - va_list_ty_s: t.CChar | t.CPtr = _type_str(builder, va_list_ptr.Ty) - arg_ty_s: t.CChar | t.CPtr = _type_str(builder, arg_ty) - line: t.CChar | t.CPtr = pool.alloc(256) - if line is None: return None - if va_list_ptr.Name is None: - return None - viperlib.snprintf(line, 256, "%s = va_arg %s %s, %s", - name, va_list_ty_s, va_list_ptr.Name, arg_ty_s) - _emit(builder, line) - return SSAValue(pool, arg_ty, name) + return builder.build_va_arg(va_list_ptr, arg_ty) # ============================================================ @@ -1279,44 +1803,7 @@ def build_inline_asm(builder: IRBuilder | t.CPtr, ret_ty: LLVMType | t.CPtr, 简化的内联汇编调用,无参数版本。 """ - if builder is None: return None - pool: memhub.MemBuddy | t.CPtr = builder.Pool - - # 判断返回类型是否为 void - is_void: t.CInt = 0 - match ret_ty: - case LLVMType.Void(): - is_void = 1 - case _: - is_void = 0 - - name: t.CChar | t.CPtr = None - if is_void == 0: - name = _alloc_ssa_name(builder) - - ret_ty_s: t.CChar | t.CPtr = _type_str(builder, ret_ty) - line: t.CChar | t.CPtr = pool.alloc(512) - if line is None: return None - - if has_side_effects != 0: - if is_void == 1: - viperlib.snprintf(line, 512, "call %s asm sideeffect \"%s\", \"%s\"()", - ret_ty_s, asm_text, constraints) - else: - viperlib.snprintf(line, 512, "%s = call %s asm sideeffect \"%s\", \"%s\"()", - name, ret_ty_s, asm_text, constraints) - else: - if is_void == 1: - viperlib.snprintf(line, 512, "call %s asm \"%s\", \"%s\"()", - ret_ty_s, asm_text, constraints) - else: - viperlib.snprintf(line, 512, "%s = call %s asm \"%s\", \"%s\"()", - name, ret_ty_s, asm_text, constraints) - _emit(builder, line) - - if is_void == 1: - return None - return SSAValue(pool, ret_ty, name) + return builder.build_inline_asm(ret_ty, asm_text, constraints, has_side_effects) # ============================================================ diff --git a/includes/llvmlite/__module.vp b/includes/llvmlite/__module.vp index 80cbfeb..9edddb9 100644 --- a/includes/llvmlite/__module.vp +++ b/includes/llvmlite/__module.vp @@ -61,6 +61,238 @@ class LLVMModule: NamedTypeCount: t.CSizeT # 命名结构体类型计数 # 总计: 12 字段 * 8 = 96 字节(无 vtable) + # ============================================================ + # 实例方法(对象化重构:原 module_* 顶层函数改写为方法, + # 原顶层函数保留为一行委托) + # ============================================================ + + # ---- 链表操作 ---- + def add_function(self, func: Function | t.CPtr): + """将函数追加到模块函数链表(O(1))""" + if self is None or func is None: return + func.Next = None + if self.FuncHead is None: + self.FuncHead = func + else: + self.FuncTail.Next = func + self.FuncTail = func + self.FuncCount += 1 + + def add_global(self, gv: GlobalVariable | t.CPtr): + """将全局变量追加到模块全局变量链表(O(1))""" + if self is None or gv is None: return + gv.Next = None + if self.GlobalHead is None: + self.GlobalHead = gv + else: + self.GlobalTail.Next = gv + self.GlobalTail = gv + self.GlobalCount += 1 + + # ---- 目标属性 ---- + def set_target(self, triple: t.CChar | t.CPtr): + """设置目标三元组""" + if self is None: return + self.TargetTriple = triple + + def set_datalayout(self, layout: t.CChar | t.CPtr): + """设置数据布局""" + if self is None: return + self.DataLayout = layout + + # ---- 命名结构体类型管理 ---- + def add_named_type(self, pool: memhub.MemBuddy | t.CPtr, ty: LLVMType | t.CPtr): + """将命名结构体类型添加到模块(用于输出 %"name" = type { ... } 定义行) + + 非命名结构体(Name 为 None 或非 Struct 类型)会被忽略。 + 按 Name 去重;同名时 opaque 声明可被完整定义替换(供跨模块值类型 alloca)。 + """ + if self 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 = self.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 + # opaque 类型插入链表头部,确保在引用它的完整定义之前声明。 + # 例如 TypeRegistry{HashTable*, ...} 中,HashTable 的 opaque 声明 + # 必须在 TypeRegistry 定义之前,否则 llc 报 "use of undefined type"。 + # 完整定义追加到链表尾部,保持原有顺序。 + if _is_opaque_struct(ty) != 0: + node.Next = self.NamedTypeHead + if self.NamedTypeHead is None: + self.NamedTypeTail = node + self.NamedTypeHead = node + else: + node.Next = None + if self.NamedTypeHead is None: + self.NamedTypeHead = node + else: + self.NamedTypeTail.Next = node + self.NamedTypeTail = node + self.NamedTypeCount += 1 + + def _is_type_defined_in_module(self, name: t.CChar | t.CPtr) -> int: + """检查模块中是否已定义指定名称的结构体类型,返回 1=已定义 / 0=未定义""" + if self is None or name is None: + return 0 + cur: NamedTypeNode | t.CPtr = self.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: + return 1 + cur = cur.Next + return 0 + + def ensure_opaque_for_type(self, pool: memhub.MemBuddy | t.CPtr, + ty: LLVMType | t.CPtr): + """为类型中的跨模块结构体引用添加 opaque 声明 + + 当类型引用了 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时, + 如果该名称未在模块中定义,则添加 opaque 声明。 + """ + _scan_type_for_cross_module_refs(pool, self, ty) + + # ---- 打印 ---- + def PrintModule(self, buf: t.CChar | t.CPtr, size: t.CSizeT, + pool: memhub.MemBuddy | t.CPtr, mode: t.CInt): + """将模块序列化为 .ll 文本写入 buf""" + if self is None or buf is None or size == 0: return + + # 头部注释(stub 和 full 模式输出,text 模式不输出) + if mode == OUTPUT_FULL or mode == OUTPUT_STUB: + _append_cstr(buf, size, "; ModuleID = '") + if self.Name is not None: + _append_cstr(buf, size, self.Name) + _append_cstr(buf, size, "'\n") + + # 目标三元组 + if self.TargetTriple is not None: + _append_cstr(buf, size, "target triple = \"") + _append_cstr(buf, size, self.TargetTriple) + _append_cstr(buf, size, "\"\n") + + # 数据布局 + if self.DataLayout is not None: + _append_cstr(buf, size, "target datalayout = \"") + _append_cstr(buf, size, self.DataLayout) + _append_cstr(buf, size, "\"\n") + + # 空行 + _append_cstr(buf, size, "\n") + + # 命名结构体类型定义(stub 和 full 模式输出,text 模式不输出) + if mode == OUTPUT_FULL or mode == OUTPUT_STUB: + # 扫描所有函数签名,为跨模块结构体引用添加 opaque 定义 + # 确保 stub.ll 包含被 declare/define 引用的跨模块类型定义 + # 修复:alloc_buf 返回 viperio.Buf,但 Buf 类型未在 NamedTypeHead 中注册 + func_scan: Function | t.CPtr = self.FuncHead + while func_scan is not None: + if func_scan.RetTy is not None: + self.ensure_opaque_for_type(pool, func_scan.RetTy) + if func_scan.Params is not None: + param_scan: Param | t.CPtr = func_scan.Params.Head + while param_scan is not None: + if param_scan.Ty is not None: + self.ensure_opaque_for_type(pool, param_scan.Ty) + param_scan = param_scan.Next + func_scan = func_scan.Next + nt: NamedTypeNode | t.CPtr = self.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: + if ty_buf is not None: + ty_buf[0] = '\0' + PrintStructDefinition(ty_buf, 4096, nt.Ty, pool) + _append_cstr(buf, size, ty_buf) + _append_cstr(buf, size, "\n") + nt = nt.Next + + if self.NamedTypeHead is not None: + _append_cstr(buf, size, "\n") + + # 全局变量 + gv: GlobalVariable | t.CPtr = self.GlobalHead + if mode == OUTPUT_STUB: + # stub 模式:输出 external global(无初值) + while gv is not None: + _print_global_stub(buf, size, gv, pool) + _append_cstr(buf, size, "\n") + gv = gv.Next + else: + # full 和 text 模式:输出完整全局变量 + # text 模式跳过无初值的全局变量(纯声明) + gv_idx: t.CInt = 0 + while gv is not None: + if mode == OUTPUT_TEXT and gv.Initializer is None: + gv = gv.Next + gv_idx += 1 + continue + _print_global(buf, size, gv, pool) + _append_cstr(buf, size, "\n") + gv = gv.Next + gv_idx += 1 + + if self.GlobalHead is not None and (mode == OUTPUT_FULL or mode == OUTPUT_STUB): + _append_cstr(buf, size, "\n") + elif self.GlobalHead is not None and mode == OUTPUT_TEXT: + # text 模式下如果输出了全局变量,也加空行 + had_global: t.CInt = 0 + gv2: GlobalVariable | t.CPtr = self.GlobalHead + while gv2 is not None: + if gv2.Initializer is not None: + had_global = 1 + break + gv2 = gv2.Next + if had_global != 0: + _append_cstr(buf, size, "\n") + + # 遍历函数 + func: Function | t.CPtr = self.FuncHead + func_idx: t.CInt = 0 + while func is not None: + if mode == OUTPUT_STUB: + # stub 模式:仅输出 declare(IsDeclared==1) + if func.IsDeclared == 1: + FunctionPrint(buf, size, func, pool) + _append_cstr(buf, size, "\n") + elif mode == OUTPUT_TEXT: + # text 模式:仅输出 define(IsDeclared==0) + if func.IsDeclared == 0: + FunctionPrint(buf, size, func, pool) + _append_cstr(buf, size, "\n") + else: + # full 模式:输出所有函数 + FunctionPrint(buf, size, func, pool) + _append_cstr(buf, size, "\n") + func = func.Next + func_idx += 1 + # ============================================================ # 工厂函数 @@ -112,39 +344,19 @@ def new_global_variable(pool: memhub.MemBuddy | t.CPtr, # 链表操作(嵌入式 Head/Tail/Count,O(1) 追加) # ============================================================ def module_add_function(mod: LLVMModule | t.CPtr, func: Function | t.CPtr): - """将函数追加到模块函数链表(O(1))""" - if mod is None or func is None: return - func.Next = None - if mod.FuncHead is None: - mod.FuncHead = func - else: - mod.FuncTail.Next = func - mod.FuncTail = func - mod.FuncCount += 1 + return mod.add_function(func) def module_add_global(mod: LLVMModule | t.CPtr, gv: GlobalVariable | t.CPtr): - """将全局变量追加到模块全局变量链表(O(1))""" - if mod is None or gv is None: return - gv.Next = None - if mod.GlobalHead is None: - mod.GlobalHead = gv - else: - mod.GlobalTail.Next = gv - mod.GlobalTail = gv - mod.GlobalCount += 1 + return mod.add_global(gv) def module_set_target(mod: LLVMModule | t.CPtr, triple: t.CChar | t.CPtr): - """设置目标三元组""" - if mod is None: return - mod.TargetTriple = triple + return mod.set_target(triple) def module_set_datalayout(mod: LLVMModule | t.CPtr, layout: t.CChar | t.CPtr): - """设置数据布局""" - if mod is None: return - mod.DataLayout = layout + return mod.set_datalayout(layout) # ============================================================ @@ -177,56 +389,7 @@ def _is_opaque_struct(ty: LLVMType | t.CPtr) -> int: def module_add_named_type(mod: LLVMModule | t.CPtr, pool: memhub.MemBuddy | t.CPtr, ty: LLVMType | t.CPtr): - """将命名结构体类型添加到模块(用于输出 %"name" = type { ... } 定义行) - - 非命名结构体(Name 为 None 或非 Struct 类型)会被忽略。 - 按 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 - # 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: - node.Next = None - if mod.NamedTypeHead is None: - mod.NamedTypeHead = node - else: - mod.NamedTypeTail.Next = node - mod.NamedTypeTail = node - mod.NamedTypeCount += 1 + return mod.add_named_type(pool, ty) # ============================================================ @@ -237,18 +400,7 @@ def module_add_named_type(mod: LLVMModule | t.CPtr, pool: memhub.MemBuddy | t.CP # 这解决了跨模块结构体引用导致的 llc "use of undefined type" 错误。 # ============================================================ def _is_type_defined_in_module(mod: LLVMModule | t.CPtr, name: t.CChar | t.CPtr) -> int: - """检查模块中是否已定义指定名称的结构体类型,返回 1=已定义 / 0=未定义""" - if mod is None or name is None: - return 0 - 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: - return 1 - cur = cur.Next - return 0 + return mod._is_type_defined_in_module(name) def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr, @@ -299,12 +451,7 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr, def module_ensure_opaque_for_type(mod: LLVMModule | t.CPtr, pool: memhub.MemBuddy | t.CPtr, ty: LLVMType | t.CPtr): - """为类型中的跨模块结构体引用添加 opaque 声明 - - 当类型引用了 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时, - 如果该名称未在模块中定义,则添加 opaque 声明。 - """ - _scan_type_for_cross_module_refs(pool, mod, ty) + return mod.ensure_opaque_for_type(pool, ty) # ============================================================ @@ -350,119 +497,7 @@ OUTPUT_TEXT: t.CDefine = 2 # 仅代码(define + global 带初值) def LLVMModulePrint(buf: t.CChar | t.CPtr, size: t.CSizeT, mod: LLVMModule | t.CPtr, pool: memhub.MemBuddy | t.CPtr, mode: t.CInt): - """将模块 mod 序列化为 .ll 文本写入 buf""" - if mod is None or buf is None or size == 0: return - - # 头部注释(stub 和 full 模式输出,text 模式不输出) - if mode == OUTPUT_FULL or mode == OUTPUT_STUB: - _append_cstr(buf, size, "; ModuleID = '") - if mod.Name is not None: - _append_cstr(buf, size, mod.Name) - _append_cstr(buf, size, "'\n") - - # 目标三元组 - if mod.TargetTriple is not None: - _append_cstr(buf, size, "target triple = \"") - _append_cstr(buf, size, mod.TargetTriple) - _append_cstr(buf, size, "\"\n") - - # 数据布局 - if mod.DataLayout is not None: - _append_cstr(buf, size, "target datalayout = \"") - _append_cstr(buf, size, mod.DataLayout) - _append_cstr(buf, size, "\"\n") - - # 空行 - _append_cstr(buf, size, "\n") - - # 命名结构体类型定义(stub 和 full 模式输出,text 模式不输出) - if mode == OUTPUT_FULL or mode == OUTPUT_STUB: - # 扫描所有函数签名,为跨模块结构体引用添加 opaque 定义 - # 确保 stub.ll 包含被 declare/define 引用的跨模块类型定义 - # 修复:alloc_buf 返回 viperio.Buf,但 Buf 类型未在 NamedTypeHead 中注册 - func_scan: Function | t.CPtr = mod.FuncHead - while func_scan is not None: - if func_scan.RetTy is not None: - module_ensure_opaque_for_type(mod, pool, func_scan.RetTy) - if func_scan.Params is not None: - param_scan: Param | t.CPtr = func_scan.Params.Head - while param_scan is not None: - if param_scan.Ty is not None: - module_ensure_opaque_for_type(mod, pool, param_scan.Ty) - 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: - if ty_buf is not None: - ty_buf[0] = '\0' - PrintStructDefinition(ty_buf, 4096, nt.Ty, pool) - _append_cstr(buf, size, ty_buf) - _append_cstr(buf, size, "\n") - nt = nt.Next - - if mod.NamedTypeHead is not None: - _append_cstr(buf, size, "\n") - - # 全局变量 - gv: GlobalVariable | t.CPtr = mod.GlobalHead - if mode == OUTPUT_STUB: - # stub 模式:输出 external global(无初值) - while gv is not None: - _print_global_stub(buf, size, gv, pool) - _append_cstr(buf, size, "\n") - gv = gv.Next - else: - # full 和 text 模式:输出完整全局变量 - # text 模式跳过无初值的全局变量(纯声明) - gv_idx: t.CInt = 0 - while gv is not None: - if mode == OUTPUT_TEXT and gv.Initializer is None: - gv = gv.Next - gv_idx += 1 - continue - _print_global(buf, size, gv, pool) - _append_cstr(buf, size, "\n") - gv = gv.Next - gv_idx += 1 - - if mod.GlobalHead is not None and (mode == OUTPUT_FULL or mode == OUTPUT_STUB): - _append_cstr(buf, size, "\n") - elif mod.GlobalHead is not None and mode == OUTPUT_TEXT: - # text 模式下如果输出了全局变量,也加空行 - had_global: t.CInt = 0 - gv2: GlobalVariable | t.CPtr = mod.GlobalHead - while gv2 is not None: - if gv2.Initializer is not None: - had_global = 1 - break - gv2 = gv2.Next - if had_global != 0: - _append_cstr(buf, size, "\n") - - # 遍历函数 - func: Function | t.CPtr = mod.FuncHead - func_idx: t.CInt = 0 - while func is not None: - if mode == OUTPUT_STUB: - # stub 模式:仅输出 declare(IsDeclared==1) - if func.IsDeclared == 1: - FunctionPrint(buf, size, func, pool) - _append_cstr(buf, size, "\n") - elif mode == OUTPUT_TEXT: - # text 模式:仅输出 define(IsDeclared==0) - if func.IsDeclared == 0: - FunctionPrint(buf, size, func, pool) - _append_cstr(buf, size, "\n") - else: - # full 模式:输出所有函数 - FunctionPrint(buf, size, func, pool) - _append_cstr(buf, size, "\n") - func = func.Next - func_idx += 1 + return mod.PrintModule(buf, size, pool, mode) # ============================================================ diff --git a/wiki/14-oo-refactor.md b/wiki/14-oo-refactor.md new file mode 100644 index 0000000..077fb95 --- /dev/null +++ b/wiki/14-oo-refactor.md @@ -0,0 +1,225 @@ +# TPV 对象化重构方案 + +> 基线:TPV `f63dcd7` / TransPyC `e1cb560`(对象化前最后稳定版本,2026-08-09) +> 状态:方案讨论稿,未开始实施 + +## 1. 背景与目标 + +### 1.1 现状问题 + +TPV 编译器当前是"**数据对象 + 行为函数式**"架构: + +- `includes/llvmlite/`:19 个数据结构类已对象化(`IRBuilder`/`LLVMModule`/`Value`/`LLVMType`…),但**所有操作是"首参传 self"的顶层函数**(`build_load(builder, ...)`、`module_add_function(mod, ...)`),与 C 风格 API 一致 +- `TransPyV/App/lib/core/Handles/`:**纯函数模块**(`translate_call(pool, builder, mod, node, funcs, count, trans)` 六参连传),且存在严重膨胀: + +| 文件 | 大小 | 行数 | +|---|---|---| +| HandlesExprCall.vp | 362KB | 7043 | +| HandlesExpr.vp | 181KB | 3715 | +| HandlesClassDef.vp | 172KB | 4010 | +| HandlesType.vp | 102KB | 2360 | + +### 1.2 重构目标 + +1. **对象化**:引入 `Generator` 生成器对象(Mixin 组合),状态集中于 `self`,与 TPC 的 `Translator + LlvmCodeGenerator` 架构对齐 +2. **膨胀治理**:`translate_call` 7043 行 God Function 按职责拆散为 `CallGen` 的方法簇 +3. **不引入 TPC 代码**:基于 TPC 设计理念 + Viper 约束自研;TPC 的 LLVMCG 仅作参考 +4. **每阶段可验证**:bootstrap 编译 + 全 Test 回归,IR 输出零变化 + +## 2. 现状盘点(已实测) + +### 2.1 llvmlite 模块(`includes/llvmlite/`,7 文件) + +| 文件 | 内容 | 对象化程度 | +|---|---|---| +| `__init__.vp` (422行) | 10 工厂函数 + `LLVMModuleCore`(27 个薄封装方法) | **已有方法化先例** | +| `__builder.vp` (1336行) | **60+ 个 `build_*` 顶层函数** + `IRBuilder`(4 字段:Pool/Func/CurBlock/Counter) | **IRBuilder 是天然状态对象,签名已按 self 优先** | +| `__types.vp` (748行) | 34 类型工厂/打印/深拷贝 + `LLVMType`(**REnum tagged union**)+ `LLVMTypeCore`(19 方法) | REnum 应保留,Core 是方法化先例 | +| `__module.vp` (565行) | 19 函数 + `LLVMModule`(12 字段容器,含 NamedType 链表) | 纯数据容器,可平移 | +| `__function.vp` (798行) | 35 函数 + `Line/BasicBlock/Param/Function`(GSListNode 链) | 纯数据结构 | +| `__values.vp` (230行) | 14 函数 + `Value`(5 字段) | 纯数据结构 | +| `__verify.vp` (399行) | 15 验证函数 + `VerifyResult` | 独立,最后迁移 | + +**关键结论**: +- **IRBuilder 对象化零状态迁移成本**:4 字段已齐,60+ 顶层函数改方法只需 `self` 替代首参,签名一一对应 +- **LLVMModule 对象化有先例**:`LLVMModuleCore`/`LLVMTypeCore` 已证明"方法化封装"可行(但仍为薄壳,核心 `module_add_named_type`/打印仍是顶层函数) +- **LLVMType 保持 REnum**:变体即"子类",不应转继承层次 + +### 2.2 演进阻力点 + +1. `@t.NoVTable` + 硬编码 SIZE(`BUILDER_SIZE=32`/`pool.alloc(96)`)强耦合——**方法化不改布局则无影响**;若转 `@t.CVTable` 需全量重写 +2. 16 个跨模块 stub 访问器(`value_get_ty`/`paramnode_get_ty`…)是规避 TransPyC stub 限制的产物——方法化后可逐步消除(**依赖编译器缺陷修复,非本方案范围**) +3. `LLVMType` 的 `pool.alloc(__sizeof__)` + `c.DerefAs` 构造模式——保留为构造方法 + +## 3. 目标架构 + +``` +includes/llvmlite/ # ① 方法化平移(P0) + ├── __builder.vp # IRBuilder + 60 方法(build_load → self.load) + ├── __module.vp # LLVMModule + 方法 + 命名类型注册 + ├── __types.vp # LLVMType(REnum) + 构造方法 + ├── __values.vp / __function.vp / __verify.vp + └── __init__.vp # 薄兼容层:旧顶层函数委托新方法(调用方零改动) + +TransPyV/App/lib/core/ + ├── Gen/ # ② 生成器对象(P1-P6) + │ ├── Generator.vp # 入口:@t.CVTable,Mixin 组合,visit 分派 + │ ├── BaseGenMixin.vp # 状态/Pool/Module/Builder/SymTab + 错误/trace + │ ├── TypeGenMixin.vp # 类型映射/注解解析(← HandlesType) + │ ├── StructGenMixin.vp # struct 注册/字段/继承/vtable(← HandlesStruct+ClassDef 布局) + │ ├── ExprGenMixin.vp # 表达式/二元/一元(← HandlesExpr+ExprOps) + │ ├── CallGenMixin.vp # 调用分派/方法/类型转换/c.*/stdarg(← HandlesExprCall 拆解) + │ ├── FuncGenMixin.vp # 函数/方法声明与翻译(← HandlesFunctions) + │ ├── ClassGenMixin.vp # 类定义/泛型特化(← HandlesClassDef) + │ ├── StmtGenMixin.vp # 语句(← Body/If/While/For/Assign/AnnAssign/Return/Match/Nonlocal) + │ └── ImportsGenMixin.vp # 导入(← HandlesImports) + └── Handles/ # ③ 迁移完成后:删除或保留为薄壳(P6 决定) +``` + +## 4. P0:llvmlite 方法化(双轨过渡) + +### 4.1 原则 + +**纯方法化平移,不改布局、不改语义**:顶层函数 → 类方法,`self` 替代首参;类保持 `@t.NoVTable` + 硬编码 SIZE(不引入 vtable 继承)。 + +### 4.2 方法映射示例 + +```python +# 现状(__builder.vp) +def build_load(builder, ty, ptr): ... +def build_gep_struct(builder, struct_ty, field_ty, ptr, field_idx): ... + +# 目标(同文件,类方法) +@t.NoVTable +class IRBuilder: + Pool, Func, CurBlock, Counter + def load(self, ty, ptr): ... # 原函数体,builder → self + def gep_struct(self, struct_ty, field_ty, ptr, field_idx): ... + +# 兼容层(__init__.vp):旧函数保留为薄包装 +def build_load(builder, ty, ptr): + return builder.load(ty, ptr) +``` + +### 4.3 覆盖清单(60+ 方法) + +| 类别 | 方法 | +|---|---| +| 内存 | alloca / alloca_aligned / alloca_at_entry / load / store | +| 整数二元 | add/sub/mul/sdiv/udiv/srem/urem/and/or/xor/shl/lshr/ashr | +| 浮点 | fadd/fsub/fmul/fdiv/frem/fneg | +| 比较 | icmp / fcmp | +| 控制流 | br / cond_br / ret / ret_void / unreachable / switch / select | +| 调用 | call / call_indirect | +| 转换 | bitcast/sext/trunc/zext/ptrtoint/inttoptr/fpext/fptrunc/fp2si/si2fp/fp2ui/ui2fp | +| GEP | gep / gep_array / gep_struct | +| Phi/原子/杂项 | phi / atomicrmw / cmpxchg / va_arg / inline_asm | + +### 4.4 Module 方法化 + +```python +def add_function(self, func): ... +def add_global(self, gv): ... +def add_named_type(self, ty): ... +def ensure_opaque_for_type(self, ty): ... +def set_target(self, triple): ... +def print_ir(self, mode): ... +``` + +### 4.5 验证 + +- bootstrap 编译 TPV(自举)通过 +- 全 Test 回归:编译产物 `.text.ll` 与基线**逐字节一致**(兼容层保证行为不变) +- 兼容层在 P6 确认无调用方后删除 + +## 5. P1-P6:Generator 对象迁移 + +### 5.1 Generator 骨架(P1) + +```python +# 采用 @t.CVTable(多态 Mixin 继承,与现有 ReturnHandle(HandlesBase.Mixin) 模式一致, +# 符合"多态类不用 NoVTable"约定;数据类 llvmlite 仍为 NoVTable) +@t.CVTable +class Generator(BaseGenMixin, TypeGenMixin, StructGenMixin, + ExprGenMixin, CallGenMixin, FuncGenMixin, + ClassGenMixin, StmtGenMixin, ImportsGenMixin): + pass +``` + +BaseGenMixin 状态字段(迁移自 HandlesTranslator): +`Pool` / `Module` / `Builder` / `SymTab` / `FuncTable` / `FuncCount` / `StructTable` / `VTableCache` / `ImportedModules` / `FromImports` / `ModuleSha1` / `CurFunc` / `CurBuilder` / `CurClassName` / 错误 trace 栈 + +### 5.2 Mixin 方法映射表(P2-P5) + +| Mixin | 来源(现状函数) | 迁移后方法 | +|---|---|---| +| TypeGenMixin | `HandlesType.map_t_type` / `resolve_annotation_type` / CDefine 表 | `self.map_type(name)` / `self.resolve_annotation(node)` | +| StructGenMixin | `HandlesStruct.register_struct` / `find_struct` / `lookup_field`;`HandlesClassDef` 布局/vtable | `self.register_struct(...)` / `self.lookup_field(...)` / `self.gen_vtable(cls)` | +| ExprGenMixin | `HandlesExpr.translate_value` / `translate_name_value` / 属性访问;`HandlesExprOps.translate_binop` / `try_unary_operator_overload` | `self.visit_value(node)` / `self.visit_binop(node)` / `self.visit_attr(node)` | +| CallGenMixin | `HandlesExprCall.translate_call`(**拆解**)+ `_translate_method_call` / `_translate_t_type_cast` / `translate_c_addr` 等 | `self.visit_call(node)` → `self._call_method(...)` / `self._call_t_cast(...)` / `self._call_c_addr(...)` | +| FuncGenMixin | `HandlesFunctions.translate_function_def` / `_mangle_name` | `self.translate_func(node)` / `self.mangle_name(name)` | +| ClassGenMixin | `HandlesClassDef.translate_class_def` / `_specialize_generic_class` | `self.translate_class(node)` / `self.specialize_generic(...)` | +| StmtGenMixin | `HandlesBody.translate_stmt` + If/While/For/Assign/AnnAssign/Return/Match/Nonlocal 各 Handle | `self.visit_stmt(node)` | +| ImportsGenMixin | `HandlesImports` Import/ImportFrom | `self.import_module(...)` | + +### 5.3 签名瘦身规则 + +```python +# 现状:六参连传 +translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans) + +# 目标:self 持状态 +self.visit_call(node) # 内部用 self.Pool/self.Builder/self.Module/self.FuncTable +``` + +- 中间态:`self.visit_call(node, funcs_ptr, func_count)`(表引用局部化后去参) +- 迁移顺序**从叶子到根**(先无依赖者),每迁移一个 Mixin 立即 bootstrap + +### 5.4 CallGen 拆解(P4,膨胀治理重点) + +`translate_call` 7043 行按分支簇拆为方法(**纯移动,逻辑零改动**): + +| 方法簇 | 对应分支 | +|---|---| +| `_call_generic_ctor(node)` | 泛型类构造 `list[int](pool, n)` | +| `_call_binop_cast(node)` | `(A|t.CPtr)(val)` BinOp 强转 | +| `_call_c_special(node)` | `c.Deref/Addr/Set/Load/Asm/LLVMIR/CIf/CIfdef` 系列 | +| `_call_stdarg(node)` | `va_arg/arg/va_start/va_end` | +| `_call_builtin_cast(node)` | `str/bytes/int/bool(x)` | +| `_call_t_cast(node)` | `t.CInt(x)` / `t.CVoid(x, CPtr)` / 裸名 typedef | +| `_call_class_sizeof(node)` | `ClassName.__sizeof__()` | +| `_call_method(node)` | `self.field.method(...)` / `(expr).method(...)` | +| `_call_plain(node)` | 普通函数/跨模块调用 | + +## 6. 迁移计划总表 + +| 阶段 | 内容 | 产出 | 验证 | +|---|---|---|---| +| P0 | llvmlite 方法化 + 兼容层 | Builder/Module 方法化 | bootstrap + 全回归(IR 零差异) | +| P1 | Generator 骨架 + trans 字段迁入 | `Gen/Generator.vp` 空状态对象 | bootstrap | +| P2 | TypeGen / StructGen | 2 个 Mixin 文件 | bootstrap | +| P3 | ExprGen(含 ExprOps) | 1-2 个 Mixin | bootstrap | +| P4 | CallGen(拆 7043 行) | 9 个方法簇 | bootstrap | +| P5 | FuncGen / ClassGen / StmtGen / ImportsGen | 4 个 Mixin | bootstrap | +| P6 | 删除 Handles 壳 / 兼容层 / ast visitor(可选) | 收尾 | bootstrap + 全回归 | + +**每阶段验收**:bootstrap 编译通过 → 全 Test 项目编译通过 → `.text.ll` 与基线 diff 为空(或仅行号差异)。 + +## 7. 风险与对策 + +| 风险 | 对策 | +|---|---| +| `@t.NoVTable` + 硬编码 SIZE 布局 | 方法化不改布局;Generator 用 `@t.CVTable`(数据类保持 NoVTable) | +| 跨模块 stub 访问器 | 方法化后保留访问器(消除依赖编译器缺陷修复,延后) | +| Viper 循环 import | 保持现有"延迟 import"模式 | +| 迁移引入行为差异 | 每阶段 `.text.ll` 基线 diff 校验 | +| REnum LLVMType 误转继承 | 明确保持 REnum 构造模式 | +| 工作量/回归风险 | 纯移动优先(P0-P5),语义重构仅 P6(可选) | + +## 8. 验收标准(最终) + +1. TPV bootstrap 编译通过(三重编译流程) +2. 全部 Test 项目编译 + 运行通过 +3. IR 输出与基线一致(无行为变化) +4. `HandlesExprCall.vp` 7043 行 → `CallGen` 各方法簇 < 1500 行 +5. 所有 Handles 调用不再六参连传(`self` 持状态)