fix(asm): 内联汇编输出约束修复(去重+多输出 struct)
- _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 后缀非法
This commit is contained in:
1 parent
244c8efc96
commit
e7b0fecb99
1 file changed
+67
-4
@@ -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,8 +1492,35 @@ 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:
|
||||
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 是目标类型
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user