Files

274 lines
10 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from stdint import *
import string
import viperlib
import stdio
import memhub
from linkedlist import GSListNode
from .__types import LLVMType, TypePrint
# ============================================================
# Value: SSA value representation
#
# Represents an LLVM IR value: an SSA temporary (%0/%result) or a constant literal (42/0).
# Each value has a type pointer, a name, and an is-constant flag.
# Inherits GSListNode[Value] to get a strongly-typed Next: Value|CPtr (the value pool list)
#
# Note: novirtual must be added (see the Line comment in __function.py for the reason).
# ============================================================
novirtual class Value(GSListNode[Value]):
Ty: LLVMType* # value type
Name: str # SSA name ("%0"/"%result") or constant text ("42"/"1.5e+00")
IsConst: int # 1 = constant literal, 0 = SSA temporary
IntVal: int64 # integer constant value (used when IsConst=1)
FloatVal: double # float constant value (used when IsConst=1 and Ty is Float)
# ============================================================
# Factory functions
# ============================================================
# Value hardcoded size: Next(8) + Ty(8) + Name(8) + IsConst(4) + IntVal(8) + FloatVal(8) = 48 bytes
define VALUE_SIZE = 48
def new_value(pool: memhub.MemBuddy*) -> Value*:
"""Allocate a zero-initialized Value from mpool."""
ptr: Value* = pool.alloc(VALUE_SIZE)
if ptr:
string.memset(ptr, 0, VALUE_SIZE)
return ptr
def ConstInt(pool: memhub.MemBuddy*, ty: LLVMType*,
val: int64, name: str) -> Value*:
"""Create an integer constant value.
ty: integer type (Int1/Int8/Int32/Int64); val: integer value; name: caller-provided constant text (e.g. "42").
"""
v: Value* = new_value(pool)
if v is None: return None
v.Ty = ty
v.Name = name
v.IsConst = 1
v.IntVal = val
return v
def ConstNull(pool: memhub.MemBuddy*, ty: LLVMType*,
name: str) -> Value*:
"""Create a null pointer constant."""
v: Value* = new_value(pool)
if v is None: return None
v.Ty = ty
v.Name = name
v.IsConst = 1
v.IntVal = 0
return v
def ConstZero(pool: memhub.MemBuddy*, ty: LLVMType*) -> Value*:
"""Create a zeroinitializer constant (zero-initialized aggregate: struct/array)."""
v: Value* = new_value(pool)
if v is None: return None
v.Ty = ty
v.Name = "zeroinitializer"
v.IsConst = 1
v.IntVal = 0
return v
def _HexDigits(out: str, pos: int, value: uint64, nibbles: int) -> int:
"""Write exactly `nibbles` hex digits (high nibble first) of value at out[pos...]; return the new pos.
逐 nibble 自己写,不用 snprintf 的 %x/%llx:现场出现过负位型被符号扩展成 16 位文本
(值 '0xffffffffc0490fdb' 配 f32 类型),MLIR 渲染阶段直接判定位宽不匹配 → 整个模块编不过。
自己写保证"位数 == nibbles",与 MLIR 侧按类型取位宽的口径**构造上一致**。
"""
digits: str = "0123456789abcdef"
i: int = 0
while i < nibbles:
shift: int = (nibbles - 1 - i) * 4
out[pos + i] = digits[int((value >> shift) & 15)]
i += 1
return pos + nibbles
def ConstFloat(pool: memhub.MemBuddy*, ty: LLVMType*,
val: double) -> Value*:
"""Create a float constant value.
ty: float type; val: float value. Name is the raw bit pattern (f16 0x%04x, f32 0x%08x,
f64 0x%016llx, f128 0x%032x hi+lo) so MLIR FloatAttr is lossless; use CRT stdio.snprintf (%g in viperlib is buggy).
位宽取类型对象自带的 LLVMType.Bits(f16/f32/f64/f128 ↔ 4/8/16/32 位十六进制),
且十六进制逐 nibble 自写(_HexDigits)⇒ 名字位数严格等于类型位数,与 MLIR 渲染器按同一
Bits 推出的期望位数一致(不再用 snprintf:它会把高位置位的 32 位值打印成 16 位)。
"""
v: Value* = new_value(pool)
if v is None: return None
v.Ty = ty
v.IsConst = 1
v.FloatVal = val
v.IntVal = 0
# 十六进制位数 = 该浮点类型的位宽/4(f16/f32/f64/f128 ↔ 4/8/16/32)。
# 位宽取 LLVMType.Bits:类型对象自带,且渲染器打印类型文本用的也是同一个 Bits
# ⇒ 名字位数与渲染器期望位数一致。
# 注意**不要**在这里调 MlirTypeText:它的第 3 个参数是"LLVM 类型文本字符串"而不是
# LLVMType*,传类型对象会得到空文本并 fail-fast
# (现场:cannot convert to MLIR type text (v1 does not cover this form): '')。
# 旧实现的真凶是 printf(对高位置位的 32 位值按符号扩展后的 64 位打印 → 16 位文本),
# 这里改成 _HexDigits 逐 nibble 写,位数严格等于 nibbles。
f_bits: int = 64
match ty:
case LLVMType.Float(ftb):
f_bits = ftb
nibbles: int = f_bits // 4
if nibbles != 4 and nibbles != 8 and nibbles != 16 and nibbles != 32: nibbles = 16
name_buf: str = pool.alloc(64)
if name_buf is not None:
name_buf[0] = '0'
name_buf[1] = 'x'
endp: int = 2
if nibbles == 4:
# half: round to half first (double -> half is fptrunc), then take its 2-byte bit pattern
h16: float16 = float16(val)
b16: uint16 = 0
*&b16 = *&h16
endp = _HexDigits(name_buf, 2, uint64(b16), 4)
elif nibbles == 8:
# f32 bit pattern must be the float 32-bit encoding, not the low 32 bits of the double
# (3.14 double=0x40091EB851EB851F, low 0x51EB851F != f32 0x4048F5C3): round to f32 first.
f32v: float = float(val)
b32: uint32 = 0
*&b32 = *&f32v
endp = _HexDigits(name_buf, 2, uint64(b32), 8)
elif nibbles == 32:
# quad: double -> fp128 is lossless (fpext); little-endian low 64 first, print big-endian
# (hi first). c.Load width follows the source pointee, so cast the source to u64* to avoid overflow.
q128: float128 = float128(val)
q8: uint8* = (uint8*)(&q128)
q_lo: uint64 = 0
q_hi: uint64 = 0
*&q_lo = *(uint64*)(q8)
*&q_hi = *(uint64*)(q8 + 8)
endp = _HexDigits(name_buf, 2, q_hi, 16)
endp = _HexDigits(name_buf, endp, q_lo, 16)
else:
fv_bits: uint64 = 0
*&fv_bits = *&val
endp = _HexDigits(name_buf, 2, fv_bits, 16)
name_buf[endp] = '\0'
v.Name = name_buf
return v
def SSAValue(pool: memhub.MemBuddy*, ty: LLVMType*,
name: str) -> Value*:
"""Create an SSA temporary value.
ty: value type; name: caller-provided SSA name (e.g. "%0"/"%result").
"""
v: Value* = new_value(pool)
if v is None: return None
v.Ty = ty
v.Name = name
v.IsConst = 0
v.IntVal = 0
return v
# ============================================================
# ValuePrint: serialize a value to MLIR text (diagnostics only)
#
# SSA value -> "%0" (operands are bare SSA names); constant -> "42 : i32" (MLIR attribute form).
# Instruction rendering MUST NOT depend on this; op rendering goes through __function.vp (constants
# must first be materialized as llvm.mlir.constant/zero/addressof ops).
# ============================================================
def ValuePrint(buf: str, size: sizet,
val: Value*, pool: memhub.MemBuddy*):
"""Serialize val into buf as MLIR text; pool is used to allocate scratch buffers for
recursive complex types (Array/Struct/Func).
"""
if val is None or buf is None or size == 0:
return
buf[0] = '\0'
if val.Name is None:
return
if val.IsConst == 0:
# SSA value: emit only the SSA name
_append_str(buf, size, val.Name)
return
# constant: "<constant text> : <MLIR type>"
_append_str(buf, size, val.Name)
_append_str(buf, size, " : ")
tp: str = pool.alloc_tagged(4096, memhub.DETSITE_VALUES_168)
if tp is not None:
TypePrint(tp, 4096, val.Ty, pool)
_append_str(buf, size, tp)
# ============================================================
# Value field accessors (let other modules bypass stub-type limits)
#
# Cross-module callers only see a stub Value (missing/wrong fields), so TransPyC silently skips
# field access; these accessors run inside this module with the full type definition.
# ============================================================
def value_get_ty(val: Value*) -> LLVMType*:
"""Get the value's type."""
if val is None:
return None
return val.Ty
def value_get_next(val: Value*) -> Value*:
"""Get the next value in the list."""
if val is None:
return None
return val.Next
def value_set_ty(val: Value*, ty: LLVMType*):
"""Set the value's type."""
if val is None:
return
val.Ty = ty
def value_set_next(val: Value*, nxt: Value*):
"""Set the next value in the list."""
if val is None:
return
val.Next = nxt
def value_set_name(val: Value*, name: str):
"""Set the value's name."""
if val is None:
return
val.Name = name
def value_set_isconst(val: Value*, is_const: int):
"""Set whether the value is a constant."""
if val is None:
return
val.IsConst = is_const
# ============================================================
# Internal helpers
# ============================================================
def _append_str(dst: str, dst_size: sizet, src: str):
"""Append src to the end of dst."""
if dst is None or src is None: return
dlen: sizet = string.strlen(dst)
# Unsigned-underflow guard: if dlen >= buffer size (buffer corrupt/dangling) return early,
# else remain = dst_size - dlen underflows and dst[dlen+i] would write past the buffer.
if dlen >= dst_size:
return
slen: sizet = string.strlen(src)
remain: sizet = dst_size - dlen
if remain <= 0: return
i: sizet = 0
while i < slen and i + 1 < remain:
dst[dlen + i] = src[i]
i += 1
dst[dlen + i] = '\0'