236 lines
11 KiB
Plaintext
236 lines
11 KiB
Plaintext
import lib.core.Gen.GenApi as GenApi
|
|
from stdint import *
|
|
import memhub
|
|
import string
|
|
import stdlib
|
|
import mlirlite
|
|
import win32.win32file as win32file
|
|
import win32.win32base as win32base
|
|
import win32.fileio as fileio
|
|
import viperlib
|
|
import sys
|
|
import ast
|
|
import lib.core.VLogger as VLogger
|
|
import lib.core.IncludesScanner as IncludesScanner
|
|
import lib.core.Gen.Translator as HandlesTranslator
|
|
import lib.Phase.BuildPipeline as BuildPipeline
|
|
import lib.Stub.StubGen.VpiReader as VpiReader
|
|
import lib.Projectrans.Utils as Utils
|
|
import lib.Projectrans.Config as Config
|
|
|
|
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
|
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
|
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
|
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
|
import lib.Stub.StubMerger as StubMerger
|
|
|
|
|
|
# StubMergerIR - module structured sidecar ({sha1}.meta) write/read, submodule checks and module-name/SHA1 mapping helpers
|
|
|
|
# WriteModuleMeta - write {sha1}.meta (structured sidecar: funcs + struct layout + vtables).
|
|
# Lines: FUNC <mangled> DEF|DECL, STRUCT <sha1>.<name> FIELDS <n> PACKED <0|1>|OPAQUE, VTABLE <sha1>.<name> SLOTS <n>.
|
|
def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha1: str) -> int:
|
|
"""Write the module's func defines, struct layouts and vtables to {sha1}.meta."""
|
|
if mod is None or temp_dir is None or sha1 is None:
|
|
VLogger.error("meta write: null argument", "Meta")
|
|
return 1
|
|
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
|
if mpath is None:
|
|
VLogger.error("meta write: path construction failed", "Meta")
|
|
return 1
|
|
mf: fileio.File = fileio.File(mpath, fileio.MODE.W)
|
|
if mf.closed:
|
|
VLogger.error(f"meta write: cannot open {mpath}", "Meta")
|
|
return 1
|
|
cur: mlirlite.Function* = mod.FuncHead
|
|
steps: sizet = 0
|
|
while cur is not None:
|
|
steps += 1
|
|
if steps > 100000:
|
|
VLogger.error("meta write: function list has a cycle or exceeded the limit", "Meta")
|
|
break
|
|
nm: str = mlirlite.function_get_name(cur)
|
|
if nm is not None:
|
|
nlen: sizet = string.strlen(nm)
|
|
line: bytes = stdlib.malloc(nlen + 16)
|
|
if line is None:
|
|
VLogger.error("meta write: line buffer allocation failed", "Meta")
|
|
break
|
|
mark: str = "DECL"
|
|
if mlirlite.function_is_declared(cur) == 0: mark = "DEF"
|
|
viperlib.snprintf(line, nlen + 16, "FUNC %s %s\n", nm, mark)
|
|
mf.write(line, string.strlen(line))
|
|
stdlib.free(line)
|
|
cur = mlirlite.function_get_next(cur)
|
|
# 2) Struct layout: STRUCT <name> FIELDS <n> PACKED <0|1> (opaque -> STRUCT <name> OPAQUE)
|
|
nt: mlirlite.NamedTypeNode* = mod.NamedTypeHead
|
|
nt_steps: sizet = 0
|
|
while nt is not None:
|
|
nt_steps += 1
|
|
if nt_steps > 100000:
|
|
VLogger.error("meta write: named-type list has a cycle or exceeded the limit", "Meta")
|
|
break
|
|
tn: str = mlirlite.get_struct_name(nt.Ty)
|
|
if tn is not None and tn[0] != '\0':
|
|
tl: sizet = string.strlen(tn)
|
|
lb: bytes = stdlib.malloc(tl + 48)
|
|
if lb is None:
|
|
VLogger.error("meta write: line buffer allocation failed", "Meta")
|
|
break
|
|
fc: int = mlirlite.get_struct_fcount(nt.Ty)
|
|
if fc < 0: viperlib.snprintf(lb, tl + 48, "STRUCT %s OPAQUE\n", tn)
|
|
else: viperlib.snprintf(lb, tl + 48, "STRUCT %s FIELDS %d PACKED %d\n", tn, fc, nt.IsPacked)
|
|
mf.write(lb, string.strlen(lb))
|
|
stdlib.free(lb)
|
|
nt = nt.Next
|
|
# 3) Vtable slots: VTABLE <class> SLOTS <n> (global <class>.vtable; Ty anonymous struct fcount = slots)
|
|
gv: mlirlite.GlobalVariable* = mod.GlobalHead
|
|
gv_steps: sizet = 0
|
|
while gv is not None:
|
|
gv_steps += 1
|
|
if gv_steps > 100000:
|
|
VLogger.error("meta write: global-variable list has a cycle or exceeded the limit", "Meta")
|
|
break
|
|
gn: str = gv.Name
|
|
if gn is not None:
|
|
gnl: sizet = string.strlen(gn)
|
|
if gnl > 7 and string.strcmp(gn + gnl - 7, ".vtable") == 0:
|
|
base_len: sizet = gnl - 7
|
|
base: bytes = stdlib.malloc(base_len + 1)
|
|
if base is not None:
|
|
string.strncpy(base, gn, base_len)
|
|
base[base_len] = '\0'
|
|
ar: int = mlirlite.get_struct_fcount(gv.Ty)
|
|
if ar < 0: ar = 0
|
|
lb2: bytes = stdlib.malloc(base_len + 40)
|
|
if lb2 is None: VLogger.error("meta write: line buffer allocation failed", "Meta")
|
|
else:
|
|
viperlib.snprintf(lb2, base_len + 40, "VTABLE %s SLOTS %d\n", base, ar)
|
|
mf.write(lb2, string.strlen(lb2))
|
|
stdlib.free(lb2)
|
|
stdlib.free(base)
|
|
gv = gv.Next
|
|
mf.close()
|
|
stdlib.free(mpath)
|
|
return 0
|
|
|
|
|
|
# _MetaHasDef - read {sha1}.meta to check for a function definition ("FUNC ... DEF" line).
|
|
# Returns: 1=has DEF (impl file); 0=readable but no DEF (decl-only); -1=missing/unreadable.
|
|
def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
|
"""Read {sha1}.meta: 1 if it has a FUNC ... DEF line, 0 if none, -1 if unreadable."""
|
|
if temp_dir is None or sha1 is None: return -1
|
|
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
|
if mpath is None: return -1
|
|
mf: fileio.File = fileio.File(mpath, fileio.MODE.R)
|
|
stdlib.free(mpath)
|
|
if mf.closed: return -1
|
|
mbuf: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
|
mbr: int64 = 0
|
|
if mbuf is not None: mbr = mf.read_all(mbuf, StubMerger.STUB_READ_BUF_SIZE)
|
|
mf.close()
|
|
if mbuf is None: return -1
|
|
if mbr <= 0:
|
|
# Empty meta: no DEF, decl-only
|
|
stdlib.free(mbuf)
|
|
return 0
|
|
if mbr < StubMerger.STUB_READ_BUF_SIZE: mbuf[mbr] = '\0'
|
|
# Scan line by line: a trailing " DEF" marks an impl file
|
|
mpos: sizet = 0
|
|
while mpos < mbr:
|
|
mls: sizet = mpos
|
|
while mpos < mbr:
|
|
if mbuf[mpos] == '\n': break
|
|
mpos += 1
|
|
mll: sizet = mpos - mls
|
|
if mpos < mbr: mpos += 1
|
|
if mll > 0 and mbuf[mls + mll - 1] == '\r': mll -= 1
|
|
if mll >= 10 and string.strncmp(mbuf + mls, "FUNC ", 5) == 0:
|
|
if mbuf[mls + mll - 4] == ' ' and string.strncmp(mbuf + mls + mll - 3, "DEF", 3) == 0:
|
|
stdlib.free(mbuf)
|
|
return 1
|
|
stdlib.free(mbuf)
|
|
return 0
|
|
|
|
|
|
# IsDeclOnlyModule - whether a module is decl-only (reads the structured definition list in {sha1}.meta).
|
|
# 1=decl-only (no FUNC ... DEF, e.g. all-pass modules, no .obj needed); 0=impl; -1=unknown/missing.
|
|
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
|
"""Whether the module is decl-only (its meta has no FUNC ... DEF line)."""
|
|
h: int = _MetaHasDef(temp_dir, td_len, sha1)
|
|
if h < 0: return -1
|
|
if h == 1: return 0
|
|
return 1
|
|
# _is_submodule_of_imported - check whether a module name is a submodule of an imported package.
|
|
# deps_buf holds only direct imports; a name with '.' is matched by its top-level package (e.g. "ast.lexer" -> 1).
|
|
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
|
|
"""Whether mod_name is a submodule of a package imported in deps_buf."""
|
|
if deps_buf is None or mod_name is None: return 0
|
|
# Find the first '.' and extract the top-level package name
|
|
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
|
|
if dot_pos is None: return 0
|
|
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
|
|
if pkg_len == 0 or pkg_len >= 256: return 0
|
|
# Build the top-level package name (truncated before '.') by temporary in-place
|
|
# truncation to avoid an allocation
|
|
saved_ch: char = mod_name[pkg_len]
|
|
mod_name[pkg_len] = '\0'
|
|
result: int = GenApi.is_module_imported(deps_buf, mod_name)
|
|
mod_name[pkg_len] = saved_ch
|
|
return result
|
|
# _BuildIncludesSha1Map - build the sha1 -> module_name map from the global in-memory store.
|
|
# Source is the store filled by PopulateSha1MapStore (no _sha1_map.txt read); returns the entry count.
|
|
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet, sha1_list: str, mod_list: str) -> int:
|
|
"""Copy the sha1 to module_name map from the global store into caller arrays."""
|
|
if sha1_list is None or mod_list is None: return 0
|
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return 0
|
|
count: int = 0
|
|
for i in range(int(StubMerger._sha1_store_count)):
|
|
if count >= StubMerger.MAX_INCLUDES: break
|
|
idx: sizet = sizet(i) * 17
|
|
idx2: sizet = sizet(i) * 64
|
|
string.strcpy(sha1_list + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
|
string.strcpy(mod_list + sizet(count) * 64, StubMerger._sha1_store_mod + idx2)
|
|
count += 1
|
|
return count
|
|
# _FindSha1ByModName - look up a SHA1 by module name (exact match) for dependency graph analysis.
|
|
# sha1_arr holds 17-byte entries, mod_arr 64-byte names; returns a pointer into sha1_arr, or None.
|
|
def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -> str:
|
|
"""Look up a SHA1 by module name, falling back to the .__init__ package form."""
|
|
if sha1_arr is None or mod_arr is None or mod_name is None: return None
|
|
if count <= 0: return None
|
|
# Pass 1: exact match
|
|
for i in range(int(count)):
|
|
idx: sizet = sizet(i) * 64
|
|
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs
|
|
# (hot spot: this function strcmp-scans the whole table per lookup).
|
|
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
|
|
sidx: sizet = sizet(i) * 17
|
|
return sha1_arr + sidx
|
|
# Pass 2: package fallback ("ast" -> "ast.__init__")
|
|
nm_len: sizet = string.strlen(mod_name)
|
|
init_buf: bytes = stdlib.malloc(nm_len + 12)
|
|
if init_buf is not None:
|
|
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
|
|
for i in range(int(count)):
|
|
idx2: sizet = sizet(i) * 64
|
|
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
|
|
sidx2: sizet = sizet(i) * 17
|
|
stdlib.free(init_buf)
|
|
return sha1_arr + sidx2
|
|
stdlib.free(init_buf)
|
|
return None
|
|
# _AddSha1ToSet - add a SHA1 to a set (deduped; count unchanged if already present).
|
|
# set_buf holds 17-byte entries; returns the new count.
|
|
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
|
|
"""Add a SHA1 to the set (deduped) and return the new count."""
|
|
if set_buf is None or sha1 is None: return count
|
|
for i in range(int(count)):
|
|
idx: sizet = sizet(i) * 17
|
|
if string.strcmp(set_buf + idx, sha1) == 0: return count
|
|
if count < StubMerger.MAX_INCLUDES_SHA1:
|
|
idx: sizet = sizet(count) * 17
|
|
string.strcpy(set_buf + idx, sha1)
|
|
return count + 1
|
|
return count
|