From 217f5f021d37cde78c4db5ae904a04df88134f55 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 15 Oct 2026 20:09:09 +0800 Subject: [PATCH] =?UTF-8?q?fix(viperc):=20=E8=A7=A3=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E8=B5=8B=E5=80=BC=E8=A1=A5=E4=BD=8D=E6=8B=B7=E8=B4=9D=EF=BC=88?= =?UTF-8?q?*a=20=3D=20*b=20=E6=8C=89=E4=BD=8D=E9=87=8D=E8=A7=A3=E9=87=8A?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E5=81=9A=E6=95=B0=E5=80=BC=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现场(math.vp sin/cos): u: uint64 = 0; xv: double = x *&u = *&xv # 位拷贝,应为 movq %xmm0, %rax ef: uint64 = (u >> 52) & 0x7FF 生成:404084 cvttsd2si %xmm0, %rax ← 把 double 数值截断成 int64 代价:u 成了数值截断结果 ⇒ ef/neg 全错;x 为负时(-1.5 → u = -1)ef 命中 0x7FF ⇒ 直接 return x - x = NaN ⇒ sin/cos 负参全废。 VPC 的 `*p = v` 目标路径无条件 coerce_to_type(rhs, pointee);TPC 侧早有该判据 (HandlesAssign.py:592 调 HandlesSpecialCall.EmitRawCopyLlvm,注释明确"位拷贝,绝不转换"), VPC 漏了 ⇒ 本次按同口径补齐。 - RHS 为 UnaryOp(Deref)(`*p`)且两侧是**等宽标量**时:按**源自身类型** load(类型与槽的 存储类型一致),再 bitcast 成目的类型存回 —— 不做任何数值转换; - 不用"按目的类型重解释的 load"(`load i64, ptr %xv`):文本层 T2「store→load 转发」按**名字** 替换,会把 `store double %x, ptr %xv` 的 %x 顶进这条 i64 行,报 `use of value '%x' expects different type than prior uses: 'i64' vs 'f64'`; - 源操作数限定非调用形状,避免与前面 rhs_val 的求值重复;源不是指针 ⇒ fail-fast; - 两侧位宽不等或含聚合 ⇒ 不做位拷贝,交回 coerce 路径(等价改动前行为,聚合位拷贝未支持)。 --- App/lib/core/Gen/AssignGenMixin.vp | 47 ++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 43f578d..a081f37 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -1348,6 +1348,20 @@ novirtual class AssignGenMixin: if string.strcmp(at.attr, "Ptr") == 0: return 1 return 0 + def _DerefPunBits(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> int: + """位型 pun(`*a = *b`)的标量位宽:浮点取浮点位宽、整数取整型位宽、指针按 64、其余返回 0。 + + 只对**两侧等宽**的标量做位拷贝(double↔u64 / float↔u32 / half↔u16 / ptr↔u64 …); + 返回 0 表示该侧不是标量(聚合等)或位宽未知 ⇒ 调用方不做位拷贝,交回 coerce 路径。 + """ + if ty is None: return 0 + fb: int = self.Gen.ExprGenBase.get_llvm_float_bits(ty) + if fb != 0: return fb + ib: int = self.Gen.ExprGenBase.get_llvm_type_bits(ty) + if ib != 0: return ib + if self.Gen.ExprGenBase.is_ptr_type(ty) != 0: return 64 + return 0 + def _is_64bit_scalar_ty(self, node: ast.AST*) -> int: """Return 1 for a 64-bit scalar type (CUInt64T/CInt64T/CSizeT).""" if node is None: return 0 @@ -3065,6 +3079,39 @@ novirtual class AssignGenMixin: pointee_w: mlirlite.LLVMType* = ptr_w.Ty.Pointee if pointee_w is None: self.Gen.TypeGenErr.fatal_error(target, "deref write target type missing (pointee is empty)", "TypeError") + # `*a = *b` 是 c.Load 语义的**位拷贝**(type-pun):把源内存按**源自身类型**读出来, + # 再按位重解释(bitcast)成目的类型存回去 —— **绝不做数值转换**。缺了它 + # `*&u = *&xv`(double → uint64)会退化成 fptosi(现场 `cvttsd2si %xmm0, %rax`) + # ⇒ IEEE 位型丢失(isnan/isinf、sin/cos 的 ef/neg 全错)。 + # 判据/口径与 TPC HandlesSpecialCall.EmitRawCopyLlvm 一致(位宽必须匹配目的)。 + # 注意**不能**写成"按目的类型重解释的 load"(`load i64, ptr %xv`):文本层 T2 + # 「store→load 转发」是按**名字**替换的,会把 `store double %x, ptr %xv` 的 `%x` + # 顶进这条 i64 行 ⇒ `use of value '%x' expects different type ... 'i64' vs 'f64'`。 + # 按源自身类型 load(类型与槽的存储类型一致)就不会踩这个问题。 + if asgn.value is not None and asgn.value.kind() == ast.ASTKind.UnaryOp: + uo_r: ast.UnaryOp* = ast.UnaryOp*(asgn.value) + # 源操作数限定为非调用形状(`&x` / `p` / `a.b` / `arr[i]` …):调用形状的指针源 + # 已被前面的 rhs_val 翻译求值过一次,这里再译会重复求值(位拷贝语义下无意义)。 + if uo_r.op == ast.OpKind.Deref and uo_r.operand is not None and uo_r.operand.kind() != ast.ASTKind.Call: + src_ptr_w: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_r.operand, None, 0, trans) + if src_ptr_w is None or src_ptr_w.Ty is None or self.Gen.ExprGenBase.is_ptr_type(src_ptr_w.Ty) == 0: + self.Gen.TypeGenErr.fatal_error(asgn.value, "the operand of unary '*' on the right-hand side of a deref bit-copy must be a pointer type (type inference degraded)", "TypeError") + src_pointee_w: mlirlite.LLVMType* = src_ptr_w.Ty.Pointee + if src_pointee_w is None: + self.Gen.TypeGenErr.fatal_error(asgn.value, "deref bit-copy source pointee type missing", "TypeError") + dst_bits_w: int = self._DerefPunBits(pool, pointee_w) + src_bits_w: int = self._DerefPunBits(pool, src_pointee_w) + if dst_bits_w != 0 and dst_bits_w == src_bits_w: + native_w: mlirlite.Value* = mlirlite.build_load(builder, src_pointee_w, src_ptr_w) + if native_w is None: + self.Gen.TypeGenErr.fatal_error(asgn.value, "deref bit-copy failed to load from the source pointer", "InternalCompilerError") + bits_w: mlirlite.Value* = mlirlite.build_bitcast(builder, native_w, pointee_w) + if bits_w is None: + self.Gen.TypeGenErr.fatal_error(asgn.value, "deref bit-copy failed to reinterpret the loaded value", "InternalCompilerError") + mlirlite.build_store(builder, bits_w, ptr_w) + continue + # 两侧位宽不等(或含聚合):这里不做位拷贝,交回下面的 coerce 路径 + # (fail-safe,即等价于改动前行为;聚合位拷贝尚未支持)。 sv_w: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, pointee_w, node, trans) mlirlite.build_store(builder, sv_w, ptr_w) continue