From e7b0fecb992dd95c86a41ab1e919ea42bb51c18b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 11 Aug 2026 22:20:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(asm):=20=E5=86=85=E8=81=94=E6=B1=87?= =?UTF-8?q?=E7=BC=96=E8=BE=93=E5=87=BA=E7=BA=A6=E6=9D=9F=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?(=E5=8E=BB=E9=87=8D+=E5=A4=9A=E8=BE=93=E5=87=BA=20struct)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _asm_add_operand 去重: 模板内嵌 AsmOut/AsmInp 与 out=/inp= 声明同一变量不再重复收集 - ret_ty 多输出: output_count>1 返回匿名 struct, 元素数=输出数 - 输出处理: 多输出 extractvalue 逐元素 store(rdrand*_step 的 val+ok) - extractvalue SSA 名改字母前缀 %asm.N.e{i} 避免数字 ID 后缀非法 --- App/lib/core/Gen/CallGenMixin.vp | 81 ++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index 0563464..66e75c2 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -1219,9 +1219,20 @@ class CallGenMixin: val: llvmlite.Value | t.CPtr, constraint: str, is_output: int) -> int: - """添加操作数到数组,返回新计数(失败返回原计数)""" + """添加操作数到数组,返回新计数(失败返回原计数) + + 去重:f-string 模板内嵌的 AsmOut/AsmInp 与 out=/inp= 声明指向 + 同一变量时会重复收集(如 vrandom.vp 的 c.Asm(f"...{AsmOut(v)}", + out=[AsmOut(v)])),同一 (Value, IsOutput) 只保留第一个, + 避免约束数多于 asm 返回元素数(llc: output constraints mismatch)。 + """ if count >= ASM_MAX_OPERANDS: return count + if val is not None: + for ei in range(count): + eop: HandlesExprCall.AsmOperand | t.CPtr = self._asm_get_operand(operands, ei) + if eop is not None and eop.Value == val and eop.IsOutput == is_output: + return count op: HandlesExprCall.AsmOperand | t.CPtr = self._asm_get_operand(operands, count) op.Value = val op.Constraint = constraint @@ -1481,15 +1492,42 @@ class CallGenMixin: input_count += 1 # 确定返回类型 + # 多输出(如 vrandom rdrand*_step 的 val+ok 两个 AsmOut)返回 + # 匿名 struct {ty1, ty2, ...},元素数 = 输出操作数(LLVM asm + # 要求输出约束数 = 返回 struct 元素数)。 ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool) if output_count > 0 and first_output_idx >= 0: - out_op: HandlesExprCall.AsmOperand | t.CPtr = self._asm_get_operand(operands, first_output_idx) - if out_op.Value is not None and out_op.Value.Ty is not None: - # alloca 的类型是指针,Pointee 是目标类型 - if out_op.Value.Ty.Pointee is not None: - ret_ty = out_op.Value.Ty.Pointee + if output_count > 1: + out_pn_head: llvmlite.ParamNode | t.CPtr = None + out_pn_last: llvmlite.ParamNode | t.CPtr = None + out_pn_n: int = 0 + for oi5 in range(operand_count): + op5: HandlesExprCall.AsmOperand | t.CPtr = self._asm_get_operand(operands, oi5) + if op5 is None or op5.IsOutput == 0: + continue + if op5.Value is not None and op5.Value.Ty is not None \ + and op5.Value.Ty.Pointee is not None: + pn5: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node( + pool, op5.Value.Ty.Pointee) + if pn5 is not None: + if out_pn_head is None: + out_pn_head = pn5 + else: + out_pn_last.Next = pn5 + out_pn_last = pn5 + out_pn_n += 1 + if out_pn_head is not None and out_pn_n > 0: + ret_ty = llvmlite.Struct(pool, out_pn_head, out_pn_n, None) else: ret_ty = llvmlite.Int32(pool) + else: + out_op: HandlesExprCall.AsmOperand | t.CPtr = self._asm_get_operand(operands, first_output_idx) + if out_op.Value is not None and out_op.Value.Ty is not None: + # alloca 的类型是指针,Pointee 是目标类型 + if out_op.Value.Ty.Pointee is not None: + ret_ty = out_op.Value.Ty.Pointee + else: + ret_ty = llvmlite.Int32(pool) # 判断 void — 直接基于 output_count 判断,避免 match 类型匹配问题 is_void: int = 1 @@ -1676,9 +1714,34 @@ class CallGenMixin: # ============================================================ if is_void == 0 and first_output_idx >= 0: result_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, ret_ty, ssa_name) - out_op2: HandlesExprCall.AsmOperand | t.CPtr = self._asm_get_operand(operands, first_output_idx) - if out_op2.Value is not None: - llvmlite.build_store(builder, result_val, out_op2.Value) + out_emit_idx: int = 0 + for oi_out in range(operand_count): + orig_o: int = new_order[oi_out] + oo: HandlesExprCall.AsmOperand | t.CPtr = self._asm_get_operand(operands, orig_o) + if oo is None or oo.IsOutput == 0: + continue + if output_count > 1: + # 多输出:extractvalue 取第 out_emit_idx 个元素后 store + if oo.Value is not None and oo.Value.Ty is not None \ + and oo.Value.Ty.Pointee is not None: + ev_name: t.CChar | t.CPtr = pool.alloc(32) + ev_line: t.CChar | t.CPtr = pool.alloc(ASM_LINE_BUF_SIZE) + if ev_name is not None and ev_line is not None: + # ssa_name 形如 "%N"(纯数字 ID),构造唯一名 + # "%asm.N.e{i}"(字母开头,避免 %N.e{i} 被解析为 + # 数字 ID + 非法后缀导致 "expected '=' after id") + viperlib.snprintf(ev_name, 32, "%%asm.%s.e%d", ssa_name + 1, out_emit_idx) + viperlib.snprintf(ev_line, ASM_LINE_BUF_SIZE, + "%s = extractvalue %s %s, %d", + ev_name, ret_ty_buf, ssa_name, out_emit_idx) + llvmlite.block_append_text(pool, builder.CurBlock, ev_line) + ev_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, oo.Value.Ty.Pointee, ev_name) + llvmlite.build_store(builder, ev_val, oo.Value) + else: + if oo.Value is not None: + llvmlite.build_store(builder, result_val, oo.Value) + out_emit_idx += 1 return result_val return None