From 508428bbc9a71da5fd43bce4a37d40638fd2c2be Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 15 Oct 2026 18:35:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(viperc):=20=E6=B5=AE=E7=82=B9=E5=B8=B8?= =?UTF-8?q?=E9=87=8F=E6=8C=89=E7=9B=AE=E6=A0=87=E7=B1=BB=E5=9E=8B/?= =?UTF-8?q?=E5=A3=B0=E6=98=8E=E5=AE=BD=E5=BA=A6=E7=9B=B4=E6=8E=A5=E5=87=BA?= =?UTF-8?q?=20f32=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=85=88=E5=87=BA=20double?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 浮点字面量在 f32 目标(float(1.5) / x: float = 1.5 / f32 形参)下直接物化 f32 常量, 负号折叠(-c)同口径;去掉旧的「f64 常量 + fptrunc」 - CDefine 浮点条目记录宽度种类(1=f64 / 2=f32):define PI: float = ... 的使用处 直接出 f32 常量,与 f32 字面量混算不再被提升成 double(现场 33 处 cvtss2sd) - 本模块注册 / pyi·vpi 跨模块重解析 / xmod 缓存三处都按注解裁决宽度,保持跨模块一致 --- App/lib/Stub/StubGen/VpiReader.vp | 8 ++- App/lib/core/Gen/AssignGenMixin.vp | 75 ++++++++++++++++++++++++++- App/lib/core/Gen/Expr/ExprGenAttr.vp | 11 ++-- App/lib/core/Gen/Expr/ExprGenCdef.vp | 35 +++++++++++-- App/lib/core/Gen/Expr/ExprGenConst.vp | 7 ++- App/lib/core/Gen/Expr/ExprGenMixin.vp | 2 +- App/lib/core/Gen/Expr/ExprGenName.vp | 11 +++- App/lib/core/Gen/Expr/ExprGenOps.vp | 9 +++- App/lib/core/Gen/GenApi.vp | 14 +++-- App/lib/core/Gen/Type/TypeGenBase.vp | 4 ++ App/lib/core/Gen/Type/TypeGenCDef.vp | 52 +++++++++++++++---- App/lib/core/Repl.vp | 3 +- 12 files changed, 202 insertions(+), 29 deletions(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 175a76a..08b4ea1 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -2092,7 +2092,10 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp if cdef_t.id is None: continue if aa_r.value.kind() == ast.ASTKind.Constant: cn_v: ast.Constant* = ast.Constant*(aa_r.value) - if cn_v.const_kind == ast.CONST_FLOAT: GenApi.register_cdefine_double(pool, cdef_t.id, cn_v.float_val) + if cn_v.const_kind == ast.CONST_FLOAT: + # 浮点宽度同样由声明注解裁决(与定义方同一套), + # 否则 `define PI: float = ...` 的使用方会把 f32 当 double + GenApi.register_cdefine_double(pool, cdef_t.id, cn_v.float_val, GenApi.cdefine_decl_float_bits(pool, aa_r.annotation)) elif cn_v.const_kind == ast.CONST_INT: cdef_val: int64 = GenApi.extract_cdefine_int_value(aa_r.value) # 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度), @@ -2421,7 +2424,8 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v if cdef_is_str == 0 and cdef_is_float != 0: # Float CDefine (e.g. FRAGMENTATION_THRESHOLD: t.CDefine = 0.7): register via # register_cdefine_double (integer extraction would fail fast). - GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val) + # 注解声明 float 时带上 f32 种类(与定义方同一套裁决)。 + GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val, GenApi.cdefine_decl_float_bits(pool, aa.annotation)) elif cdef_is_str == 0: cdef_val: int64 = GenApi.extract_cdefine_int_value(aa.value) # 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 05ed170..43f578d 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -407,6 +407,60 @@ novirtual class AssignGenMixin: """Return the bit width of a CDefine declaration's explicit type T.""" return self._annot_int_bits(pool, self._define_explicit_annot(annot)) + # cdefine_decl_float_bits - define 声明的**浮点**宽度裁决(定义方与跨模块使用方共用同一套) + def cdefine_decl_float_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*) -> int: + """1 = 注解显式声明了 32 位 float(`define PI: float = ...`),0 = double / 未声明 / 非浮点。 + + 与 cdefine_decl_bits(整型位宽)并列:浮点常量的宽度只由注解决定(右侧字面量在 + .vpi 里只剩 `= 3.14159...`,已丢失 `float(...)` 包裹),故这里只看注解。 + """ + return self._annot_float_kind(pool, self._define_explicit_annot(annot)) + + def _annot_float_kind(self, pool: memhub.MemBuddy*, annot: ast.AST*) -> int: + """注解节点 → 1(f32)/ 0(其余);成分与 _annot_int_bits 对齐。""" + if annot is None: return 0 + nm: str = None + k: int = annot.kind() + if k == ast.ASTKind.Attribute: + at: ast.Attribute* = ast.Attribute*(annot) + nm = at.attr + elif k == ast.ASTKind.Name: + n2: ast.Name* = ast.Name*(annot) + nm = n2.id + if nm is None: return 0 + return self._float_kind_of_type_name(pool, nm, 0) + + def _float_kind_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int: + """类型名 → 1 表示它是 32 位 float(f32),0 表示 double 或非浮点/未知。depth 防 typedef 环。 + + 覆盖:内建名 float、`t.CFloat` / `t.CFloat32T`(map_t_type)、用户/include typedef(递归展开)。 + """ + if nm is None or depth > 10: return 0 + # 去掉模块/类限定前缀:`t.CFloat` → `CFloat`;`stdint.float32` → `float32` + dot: str = string.strrchr(nm, 46) + if dot is not None and dot[1] != '\0': + return self._float_kind_of_type_name(pool, dot + 1, depth + 1) + if string.strcmp(nm, "float") == 0: return 1 + if string.strcmp(nm, "float32") == 0: return 1 + ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, nm) + if ty is not None: + match ty: + case mlirlite.LLVMType.Float(bits): + if bits == 32: return 1 + return 0 + case _: + return 0 + # 用户/include typedef(如 `MYF: t.CTypedef = float`):展开后再递归判定 + underlying: str = self.Gen.TypeGenCType.lookup_ctypedef(nm) + if underlying is None: + underlying = self.Gen.TypeGenResolve._lookup_builtins_typedef(pool, nm) + if underlying is not None: + expanded: str = self.Gen.TypeGenResolve._expand_typedef_ref(pool, underlying) + if expanded is not None: underlying = expanded + if underlying is None: return 0 + if string.strcmp(underlying, nm) == 0: return 0 + return self._float_kind_of_type_name(pool, underlying, depth + 1) + def _cdefine_int_literal_text(self, name: str) -> str: """Return the raw value text of a CDefine line, or None if not found.""" if name is None: return None @@ -581,6 +635,25 @@ novirtual class AssignGenMixin: def _register_cdefine_from_value(self, pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None) -> None: """Register a CDefine constant (double for floats, else integer).""" if pool is None or name is None: return + # 浮点宽度种类(f32 与否)必须**在拆掉右侧包裹前**判定: + # ① 注解有显式类型(`define PI: float = ...` / `PI: float | t.CDefine = ...`)→ 注解说了算 + # (`define X: double = float(1.5)` 按 double 走); + # ② 注解没有显式类型(`PI: t.CDefine = float(3.14)`)→ 看右侧强转包裹 float(...) / double(...)。 + f32_kind: int = 0 + if self._define_explicit_annot(annot) is not None: + f32_kind = self.cdefine_decl_float_bits(pool, annot) + elif val_node is not None and val_node.kind() == ast.ASTKind.Call: + cl_w: ast.Call* = ast.Call*(val_node) + if cl_w.func is not None: + fk_w: int = cl_w.func.kind() + wname_w: str = None + if fk_w == ast.ASTKind.Name: + fn_w: ast.Name* = ast.Name*(cl_w.func) + wname_w = fn_w.id + elif fk_w == ast.ASTKind.Attribute: + fa_w: ast.Attribute* = ast.Attribute*(cl_w.func) + wname_w = fa_w.attr + if wname_w is not None and string.strcmp(wname_w, "float") == 0: f32_kind = 1 # Call-wrapped constant (e.g. float(3.14)): use its first argument as the value if val_node is not None and val_node.kind() == ast.ASTKind.Call: cl_cd: ast.Call* = ast.Call*(val_node) @@ -610,7 +683,7 @@ novirtual class AssignGenMixin: # Source recovery failed: fail fast to avoid registering a corrupted float self.Gen.TypeGenErr.fatal_error(val_node, "CDefine float value source-file recovery failed", "InternalCompilerError") return - self.Gen.TypeGenCDef.register_cdefine_double(pool, name, use_val) + self.Gen.TypeGenCDef.register_cdefine_double(pool, name, use_val, f32_kind) return if val_node is None: # CDefine declaration without a value node: fail fast diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 46aa82c..09dd85c 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -445,7 +445,10 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): if cdef_str_v is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_str_v, trans) # cross-module float CDefine (e.g. vipermath.U_M_PI) → float constant dchk_ma: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr) - if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_ma) + # f32 声明的常量直接出 f32 常量(否则 f32 字面量会被提升成 double) + if self.Gen.TypeGenCDef.is_cdefine_double() != 0: + if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_ma) + return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_ma) return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_ma_val) # check whether nm_ma.id.at.attr is an imported submodule (e.g. win32.win32file); # for a submodule do not create a global, let the nested attribute path handle it @@ -561,9 +564,11 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf) cdef_val_nested = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, tmp_fi) if self.Gen.TypeGenCDef.is_cdefine_found() != 0: - # cross-module float CDefine → float constant + # cross-module float CDefine → float constant (f32 声明直接出 f32) dchk_nst: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr) - if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nst) + if self.Gen.TypeGenCDef.is_cdefine_double() != 0: + if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nst) + return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nst) return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_val_nested) # CDefine not found: fail-fast and stop compiling (no more undefined global # references, which would make llc report "use of undefined value"). diff --git a/App/lib/core/Gen/Expr/ExprGenCdef.vp b/App/lib/core/Gen/Expr/ExprGenCdef.vp index 578172a..bcdc143 100644 --- a/App/lib/core/Gen/Expr/ExprGenCdef.vp +++ b/App/lib/core/Gen/Expr/ExprGenCdef.vp @@ -95,6 +95,24 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): return None else: pf_chk.close() return buf + # _pyi_annot_float_kind - 从 .vpi/.pyi 行的注解段(`NAME:` 到 `=` 之间)判定浮点宽度种类: + # 独立单词 float → 1(f32);double / 未声明 / 非浮点 → 0。词边界判定避免命中 `double`/`CFloat32`。 + def _pyi_annot_float_kind(self, buf: bytes, start: sizet, end: sizet) -> int: + """Return 1 when the annotation text [start,end) declares a 32-bit `float`, else 0.""" + i: sizet = start + while i + 5 <= end: + if (buf[i] == 'f' and buf[i + 1] == 'l' and buf[i + 2] == 'o' and buf[i + 3] == 'a' and buf[i + 4] == 't'): + ok_before: int = 1 + ok_after: int = 1 + if i > start: + pb: char = buf[i - 1] + if (pb >= 'A' and pb <= 'Z') or (pb >= 'a' and pb <= 'z') or (pb >= '0' and pb <= '9') or pb == '_': ok_before = 0 + if i + 5 < end: + pa: char = buf[i + 5] + if (pa >= 'A' and pa <= 'Z') or (pa >= 'a' and pa <= 'z') or (pa >= '0' and pa <= '9') or pa == '_': ok_after = 0 + if ok_before != 0 and ok_after != 0: return 1 + i += 1 + return 0 # _lookup_cross_module_cdefine - cross-module CDefine lookup: resolve the source module # from from_imports, parse its pyi "NAME: t.CDefine = value", then cache in the current module. def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*, name: str, from_imports: str, imported_modules: str = None, depth: int = 0, node: ast.AST* = None) -> int64: @@ -147,7 +165,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): if self.Gen.TypeGenCDef.is_cdefine_str() != 0: self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval) elif self.Gen.TypeGenCDef.is_cdefine_double() != 0: xd_v: double = string.atof(xval) - self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v) + # 缓存里带宽度种类(f32 声明 → 1),否则使用方会把 f32 常量当 double 物化 + xd_f32: int = self.Gen.TypeGenCDef.is_cdefine_float32() + self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v, xd_f32) else: xv_i: int64 = string.atoll(xval) self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i) @@ -286,6 +306,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): result_val: int64 = 0 result_found: int = 0 res_is_double: int = 0 + # 浮点宽度种类(1 = 注解声明为 32 位 float):跨模块缓存与本地注册都要带上, + # 否则使用方把 f32 常量当 double 物化(`define PI: float = ...` 现场)。 + res_is_f32: int = 0 res_is_str: int = 0 while pos < total_len: # find line start @@ -321,6 +344,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): eq_pos: sizet = line_start + name_len + 1 while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=': eq_pos += 1 if eq_pos >= line_start + line_len: continue + # 注解段 = [名字冒号后, '='),用于判定声明的浮点宽度(如 `PI: float | t.CDefine`) + annot_start: sizet = line_start + name_len + 1 + annot_end: sizet = eq_pos eq_pos += 1 # skip '=' # skip spaces @@ -578,7 +604,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): if is_float_val != 0: # float CDefine (e.g. U_M_PI = 3.14159...): cache as double for double # constant generation; int part serves the int path (is_cdefine_double flag). - self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl) + # 注解声明为 float 时带上 f32 种类,使用方才能直接出 f32 常量。 + res_is_f32 = self._pyi_annot_float_kind(pyi_buf, annot_start, annot_end) + self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl, res_is_f32) result_val = lval result_found = 1 res_is_double = 1 @@ -800,7 +828,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): cbuf_d: str = pool.alloc(64) if cbuf_d is not None: stdio.snprintf(cbuf_d, 64, "%.17g", dval_cc) - self.Gen.TypeGenCDef.register_xmod_cdefine(ckey2, cbuf_d, 1, 0) + # 浮点种类随缓存一起带上(1=f64 / 2=f32),供后续使用方直接物化宽度 + self.Gen.TypeGenCDef.register_xmod_cdefine(ckey2, cbuf_d, self.Gen.TypeGenCDef.xmod_float_kind(), 0) self.Gen.TypeGenCDef.set_cdefine_found(1) else: self.Gen.TypeGenCDef.set_cdefine_found(0) diff --git a/App/lib/core/Gen/Expr/ExprGenConst.vp b/App/lib/core/Gen/Expr/ExprGenConst.vp index 36fff3c..d41e095 100644 --- a/App/lib/core/Gen/Expr/ExprGenConst.vp +++ b/App/lib/core/Gen/Expr/ExprGenConst.vp @@ -105,7 +105,12 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase): if iv > 2147483647 or iv < -2147483648: return mlirlite.const_int64(pool, iv) return mlirlite.const_int32(pool, iv) elif cn.const_kind == ast.CONST_FLOAT: - # Float constants default to double (64-bit); coerce_to_type fptruncs on assignment + # 目标类型是 32 位浮点(float(1.5) / x: float = 1.5 / f32 形参)→ 直接出 f32 常量。 + # 旧实现无视 target_ty 一律先出 double 常量、再由 coerce_to_type fptrunc, + # 现场:float(2.0) / float(1.0) 生成 f64 常量 + fptrunc,与双精度常量混算时又多出 fpext。 + # 其余情况(无目标类型 / double / 非浮点)保持 double 常量,由 coerce_to_type 负责转换。 + if target_ty is not None and self.get_llvm_float_bits(target_ty) == 32: + return mlirlite.ConstFloat(pool, target_ty, cn.float_val) double_ty: mlirlite.LLVMType* = mlirlite.Double(pool) return mlirlite.ConstFloat(pool, double_ty, cn.float_val) elif cn.const_kind == ast.CONST_CHAR: diff --git a/App/lib/core/Gen/Expr/ExprGenMixin.vp b/App/lib/core/Gen/Expr/ExprGenMixin.vp index c844359..5ebbd65 100644 --- a/App/lib/core/Gen/Expr/ExprGenMixin.vp +++ b/App/lib/core/Gen/Expr/ExprGenMixin.vp @@ -51,7 +51,7 @@ novirtual class ExprGenMixin(ExprGenBase.ExprGenBase): # M2.4 `new stack|heap|memgr(x) [slife|hlife] T(args)`:只换"取存储",其余复用构造序列 elif k == ast.ASTKind.New: return self.Gen.CallGenCtor._translate_new_expr(pool, builder, mod, ast.New*(node), trans) elif k == ast.ASTKind.Compare: return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans) - elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans) + elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans, target_ty) elif k == ast.ASTKind.BoolOp: return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans) elif k == ast.ASTKind.Subscript: return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans) elif k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans) diff --git a/App/lib/core/Gen/Expr/ExprGenName.vp b/App/lib/core/Gen/Expr/ExprGenName.vp index b029a46..a729556 100644 --- a/App/lib/core/Gen/Expr/ExprGenName.vp +++ b/App/lib/core/Gen/Expr/ExprGenName.vp @@ -55,7 +55,11 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase): cdef_sv: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id) if cdef_sv is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv, trans) self.Gen.TypeGenErr.fatal_error(node, f"CDefine string value missing: {nm_id}", "InternalCompilerError") - if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), cdef_val_d) + # 浮点常量:声明为 32 位 float(`define PI: float = ...`)时直接出 f32 常量, + # 否则出 f64 —— 出 f64 会把后续 f32 字面量整体提升成 double(fpext/cvtss2sd)。 + if self.Gen.TypeGenCDef.is_cdefine_double() != 0: + if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), cdef_val_d) + return mlirlite.ConstFloat(pool, mlirlite.Double(pool), cdef_val_d) # Integer CDefine: re-look-up the full 64-bit value (a double round-trip loses # precision and would generate a wrong constant). cdef_val_c: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm_id) @@ -81,7 +85,10 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase): # Cross-module float CDefine is cached as double; return a float constant # rather than a truncated integer. dchk_nm: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id) - if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nm) + # 同本模块路径:f32 声明的常量直接出 f32 常量(跨模块缓存已带宽度种类)。 + if self.Gen.TypeGenCDef.is_cdefine_double() != 0: + if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nm) + return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nm) # Same use-site width rule (cross-module CDefine) uw_x: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val, target_ty, use_ctx) if uw_x is not None: return uw_x diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index ab972f4..49055bf 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -654,7 +654,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): return mlirlite.build_icmp(builder, predicate, lhs, rhs) # translate_unaryop - UnaryOp(op, operand) # ============================================================ - def translate_unaryop(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*: + def translate_unaryop(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*: """Translate unary operations (-x, +x, not x, ~x).""" uo: ast.UnaryOp* = ast.UnaryOp*(node) if uo is None: return None @@ -688,7 +688,12 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): operand_fbits: int = self.get_llvm_float_bits(operand.Ty) if operand_fbits > 0: # 编译期常量折叠:-c 直接产出常量并保持 IsConst - if operand.IsConst == 1: return mlirlite.ConstFloat(pool, operand.Ty, -operand.FloatVal) + if operand.IsConst == 1: + # 目标类型是 32 位浮点(`float(-3.5)` / `x: float = -3.5`)→ 直接出 f32 常量, + # 不再"f64 常量 + fptrunc"(与 ExprGenConst.translate_constant 的浮点字面量同口径)。 + if target_ty is not None and self.get_llvm_float_bits(target_ty) == 32: + return mlirlite.ConstFloat(pool, target_ty, -operand.FloatVal) + return mlirlite.ConstFloat(pool, operand.Ty, -operand.FloatVal) return mlirlite.build_fneg(builder, operand) # 编译期常量折叠:-c 直接产出常量(IsConst=1)。否则 `v: int8 = -50` 会被当成运行时 i32 值, # 触发“隐式窄化禁止”,尽管其数值完全落在 i8 内(隐式窄化的“编译期常量”豁免失效)。 diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index 5cb8dab..86329ee 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -192,9 +192,17 @@ def cdefine_decl_bits(pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.A # ---------------- register_cdefine_double (delegates to TypeGenCDef.register_cdefine_double) ---------------- -def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double): - """Delegate to gen.register_cdefine_double""" - if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value) +def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0): + """Delegate to gen.register_cdefine_double (is_f32: 1=float 声明,0=double)""" + if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value, is_f32) + + +# ---------------- cdefine_decl_float_bits (delegates to AssignGenMixin.cdefine_decl_float_bits) ---------------- +def cdefine_decl_float_bits(pool: memhub.MemBuddy*, annot: ast.AST*) -> int: + """define 声明的**浮点**宽度裁决:注解显式写了 float → 1(f32),double/未写 → 0。 + 跨模块(.vpi 重解析 define 的 AST)注册必须用它,否则使用方把 f32 常量当 double 物化。""" + if _g_gen is not None: return _g_gen.AssignGen.cdefine_decl_float_bits(pool, annot) + return 0 # ---------------- is_cdefine_annotation (delegates to AssignGenMixin.is_cdefine_annotation) ---------------- diff --git a/App/lib/core/Gen/Type/TypeGenBase.vp b/App/lib/core/Gen/Type/TypeGenBase.vp index c048ddd..2c025d9 100644 --- a/App/lib/core/Gen/Type/TypeGenBase.vp +++ b/App/lib/core/Gen/Type/TypeGenBase.vp @@ -45,6 +45,9 @@ novirtual class TypeGenBase: # --- Global CDefine compile-time constant table (was _g_cdefine_*) --- CDefineNames: list[str]* CDefineValues: list[str]* + # 浮点常量的**宽度种类**:1=f64(double,默认),2=f32(float);0=非浮点(整型/字符串)。 + # 判定"是不是浮点常量"一律用 `!= 0`(既有调用处口径不变),只有浮点常量物化处区分 64/32: + # `define PI: float = 3.14159...` 必须直接出 f32 常量,否则与 f32 字面量混算会多出 fpext。 CDefineDoubleFlags: list[int]* CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2") # 值表达式的**位宽**(bit);0=未知(按值推断)。来源:右侧显式强转(如 WORD(0x0080))优先, @@ -63,6 +66,7 @@ novirtual class TypeGenBase: # Not cleared by reset_to(mark2)/clear_cdefine_constants, so CONST_INT lookup happens once. XmodCdefNames: list[str]* XmodCdefValues: list[str]* + # 同 CDefineDoubleFlags 的编码:1=f64,2=f32,0=非浮点。 XmodCdefDoubleFlags: list[int]* XmodCdefStrFlags: list[int]* diff --git a/App/lib/core/Gen/Type/TypeGenCDef.vp b/App/lib/core/Gen/Type/TypeGenCDef.vp index 6a8f251..0e1cc6e 100644 --- a/App/lib/core/Gen/Type/TypeGenCDef.vp +++ b/App/lib/core/Gen/Type/TypeGenCDef.vp @@ -21,7 +21,13 @@ define CDEFINE_IDX_CAP = 4096 # Index hash constants (32-bit FNV-1a) define CDEF_IDX_FNV_OFFSET = 2166136261 define CDEF_IDX_FNV_PRIME = 16777619 -define CDEF_IDX_FNV_MASK = 4294967295 +define CDEF_IDX_FNV_MASK = 4294967295 + +# CDefine 浮点常量的宽度种类(CDefineDoubleFlags / XmodCdefDoubleFlags 条目编码)。 +# 1=f64(double,默认),2=f32(float);0 表示非浮点。既有读取处一律按 `!= 0` 判定 +# "是浮点常量",故 f32 天然兼容;只有浮点常量的物化处需要区分 64/32。 +define CDEFINE_FLOAT_KIND_F64 = 1 +define CDEFINE_FLOAT_KIND_F32 = 2 # TypeGenCDef - split from TypeGenMixin (stage D): CDefine compile-time constant table (register/lookup/is/set/clear) @@ -148,8 +154,12 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0) if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0) if self.CDefineBits is not None: self.CDefineBits.append(bits) - def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double) -> None: - """Register a float CDefine constant; store the value as %f text and flag it.""" + def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0) -> None: + """Register a float CDefine constant; store the value as %f text and flag it. + + is_f32: 1 表示该常量声明为 32 位 float(`define PI: float = ...` / `float(...)`), + 物化时直接出 f32 常量;0(默认)表示 double。 + """ if name is None: return if self.CDefineNames is None: tp: memhub.MemBuddy* = self._cdefine_table_pool(pool) @@ -185,7 +195,10 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): stdio.snprintf(val_buf, 32, "%.17g", value) self.CDefineValues.append(val_buf) else: self.CDefineValues.append("0") - if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(1) + # 浮点宽度种类:f32=2,f64=1(判定处一律 `!= 0`,故与旧数据兼容)。 + if self.CDefineDoubleFlags is not None: + if is_f32 != 0: self.CDefineDoubleFlags.append(CDEFINE_FLOAT_KIND_F32) + else: self.CDefineDoubleFlags.append(CDEFINE_FLOAT_KIND_F64) if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0) if self.CDefineBits is not None: self.CDefineBits.append(0) # fail-fast: a failed CDefine value buffer must abort compilation @@ -241,7 +254,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): self.CDefineFound = 1 # Cross-module CDefine lookup cache (PermPool-allocated, reused across files) def register_xmod_cdefine(self, name: str, value_str: str, is_double: int, is_str: int) -> None: - """Cache a cross-module CDefine lookup result in the perm_pool global cache.""" + """Cache a cross-module CDefine lookup result in the perm_pool global cache. + + is_double 沿用 CDefineDoubleFlags 的编码(1=f64,2=f32,0=非浮点), + 这样跨模块读取 float 声明的常量时也能保持 f32。 + """ if name is None or value_str is None: return # Dedupe: skip if cached (first lookup wins; CDefines are globally unique) if self.XmodCdefNames is not None: @@ -285,7 +302,8 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): if val_str is not None: self.CDefineFound = 1 if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1: self.CDefineIsStr = 1 - elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) == 1: self.CDefineIsDouble = 1 + elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) != 0: + self.CDefineIsDouble = int(self.XmodCdefDoubleFlags.get(i)) return val_str return None def lookup_cdefine_constant(self, name: str) -> int64: @@ -316,8 +334,9 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): # callers should use lookup_cdefine_string instead. self.CDefineIsStr = 1 return 0 - if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1: - self.CDefineIsDouble = 1 + if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) != 0: + # 浮点常量(f64=1 / f32=2):把种类原样带给 is_cdefine_double()/is_cdefine_float32() + self.CDefineIsDouble = int(self.CDefineDoubleFlags.get(i)) return string.atoll(val_str) return string.atoll(val_str) return 0 @@ -368,8 +387,9 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1: self.CDefineIsStr = 1 return 0.0 - if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1: - self.CDefineIsDouble = 1 + if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) != 0: + # 种类原样带回(1=f64 / 2=f32),物化处据此决定出 f64 还是 f32 常量。 + self.CDefineIsDouble = int(self.CDefineDoubleFlags.get(i)) ret_d: double = string.atof(val_str) return ret_d ret_i2: double = double(string.atoll(val_str)) @@ -409,6 +429,18 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): def is_cdefine_double(self) -> int: """Whether the last CDefine hit was a float constant.""" return self.CDefineIsDouble + def is_cdefine_float32(self) -> int: + """Whether the last CDefine hit was an f32 (`float`) constant; 0 = f64/非浮点。 + + 物化浮点常量时必须先看本函数:`define PI: float = ...` 要直接出 f32 常量, + 否则与 f32 字面量混算会被提升成 double(现场:33 处 cvtss2sd)。 + """ + if self.CDefineIsDouble == CDEFINE_FLOAT_KIND_F32: return 1 + return 0 + def xmod_float_kind(self) -> int: + """跨模块缓存写入用的浮点种类(1=f64 / 2=f32):把编码留在本模块,写入方不必跨模块引用 define。""" + if self.CDefineIsDouble == CDEFINE_FLOAT_KIND_F32: return CDEFINE_FLOAT_KIND_F32 + return CDEFINE_FLOAT_KIND_F64 def is_cdefine_found(self) -> int: """Whether the last CDefine lookup found a value.""" return self.CDefineFound diff --git a/App/lib/core/Repl.vp b/App/lib/core/Repl.vp index 442930c..5e8050a 100644 --- a/App/lib/core/Repl.vp +++ b/App/lib/core/Repl.vp @@ -571,7 +571,8 @@ def ReplBaselineCapture() -> int: if val is None: continue kind: int = 0 if cdef_str is not None and int(cdef_str.get(i)) == 1: kind = 2 - elif cdef_dbl is not None and int(cdef_dbl.get(i)) == 1: kind = 1 + # 浮点条目:1=f64 / 2=f32(见 TypeGenCDef 的 CDEFINE_FLOAT_KIND_*),非 0 即浮点 + elif cdef_dbl is not None and int(cdef_dbl.get(i)) != 0: kind = 1 nc: str = _repl_str_dup(_perm_pool, nm) vc: str = _repl_str_dup(_perm_pool, val) if nc is None or vc is None: continue