356 lines
11 KiB
Python
356 lines
11 KiB
Python
|
|
"""
|
|||
|
|
GAML — Glyph-Aligned Memory Layout
|
|||
|
|
|
|||
|
|
Deterministic memory layout aligned to glyph offsets for compressed GX execution.
|
|||
|
|
|
|||
|
|
Maps glyph IDs to memory regions based on:
|
|||
|
|
- Glyph priority (higher priority = lower address offset)
|
|||
|
|
- Glyph band (A/B/C/D determines segment size class)
|
|||
|
|
- Glyph score (determines capacity within the region)
|
|||
|
|
- Specialized type (aether_node, monument_grade, etc. get reserved spans)
|
|||
|
|
|
|||
|
|
The layout is fully deterministic — same glyph set always produces the same memory map,
|
|||
|
|
guaranteeing reproducible execution across runs.
|
|||
|
|
|
|||
|
|
Integration points:
|
|||
|
|
- Glyph registry (glyphs/super_registry.py): reads glyph data for layout calculations
|
|||
|
|
- Specialized types (glyphs/specialized_types.py): type-specific memory constraints
|
|||
|
|
- XIC VM context (xic_ops.py): XICContext._state stores the active memory layout
|
|||
|
|
- Segment runtime (xic_extensions/segment_runtime.py): segments are loaded into layout
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import logging
|
|||
|
|
from dataclasses import dataclass, field
|
|||
|
|
from typing import Any, Dict, List, Optional, Tuple
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
# Layout constants
|
|||
|
|
PAGE_SIZE = 256
|
|||
|
|
RESERVED_BASE = 0x0000
|
|||
|
|
AETHER_NODE_BASE = 0x0100
|
|||
|
|
MONUMENT_BASE = 0x1000
|
|||
|
|
STANDARD_BASE = 0x4000
|
|||
|
|
STACK_BASE = 0xF000
|
|||
|
|
MAX_ADDRESS = 0xFFFF
|
|||
|
|
|
|||
|
|
BAND_SIZE_MULTIPLIERS = {
|
|||
|
|
"A": 16,
|
|||
|
|
"B": 8,
|
|||
|
|
"C": 4,
|
|||
|
|
"D": 2,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class MemoryRegion:
|
|||
|
|
"""A contiguous memory region assigned to a glyph.
|
|||
|
|
|
|||
|
|
Attributes:
|
|||
|
|
glyph_id: The glyph this region belongs to.
|
|||
|
|
base: Base address (16-bit).
|
|||
|
|
size: Size in bytes.
|
|||
|
|
band: The glyph's band ("A"–"D").
|
|||
|
|
priority: Glyph priority (higher = more favorable placement).
|
|||
|
|
label: Human-readable label for debugging.
|
|||
|
|
type: Region type ("code", "data", "stack", "reserved").
|
|||
|
|
permissions: Access permissions ("rw", "rx", "r").
|
|||
|
|
"""
|
|||
|
|
glyph_id: str
|
|||
|
|
base: int
|
|||
|
|
size: int
|
|||
|
|
band: str
|
|||
|
|
priority: float
|
|||
|
|
label: str = ""
|
|||
|
|
type: str = "code"
|
|||
|
|
permissions: str = "rx"
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class GlyphAlignedMemoryLayout:
|
|||
|
|
"""
|
|||
|
|
Deterministic memory layout built from a set of glyph IDs.
|
|||
|
|
|
|||
|
|
Layout algorithm:
|
|||
|
|
1. Sort glyphs by priority descending
|
|||
|
|
2. Allocate regions: AETHER_NODE_BASE → MONUMENT_BASE → STANDARD_BASE
|
|||
|
|
3. Within each tier, allocate in band order (A→D), then by priority
|
|||
|
|
4. Each region is PAGE_SIZE * band_multiplier bytes
|
|||
|
|
5. Stack region at STACK_BASE with reserved span
|
|||
|
|
6. Result is fully deterministic for the same input set
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
regions: List[MemoryRegion] = field(default_factory=list)
|
|||
|
|
glyph_map: Dict[str, MemoryRegion] = field(default_factory=dict)
|
|||
|
|
total_size: int = 0
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def build(
|
|||
|
|
cls,
|
|||
|
|
glyph_ids: List[str],
|
|||
|
|
glyph_data: Optional[Dict[str, Any]] = None,
|
|||
|
|
) -> "GlyphAlignedMemoryLayout":
|
|||
|
|
"""
|
|||
|
|
Construct a memory layout for the given glyph IDs.
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
glyph_ids: List of glyph IDs to lay out (e.g. ["G001", "G015", "G042"]).
|
|||
|
|
glyph_data: Optional dict of glyph_id → glyph dict with
|
|||
|
|
priority, band, score, specialized_type fields.
|
|||
|
|
If None, loads from super_registry.
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
GlyphAlignedMemoryLayout with regions allocated.
|
|||
|
|
"""
|
|||
|
|
if glyph_data is None:
|
|||
|
|
glyph_data = cls._load_glyph_data(glyph_ids)
|
|||
|
|
|
|||
|
|
tiered: Dict[str, List[Tuple[str, Dict[str, Any]]]] = {
|
|||
|
|
"aether": [],
|
|||
|
|
"monument": [],
|
|||
|
|
"standard": [],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for gid in glyph_ids:
|
|||
|
|
data = glyph_data.get(gid, {})
|
|||
|
|
stype = data.get("specialized_type", "")
|
|||
|
|
if stype == "aether_node" or gid == "G001":
|
|||
|
|
tiered["aether"].append((gid, data))
|
|||
|
|
elif stype == "monument_grade_equilibrium":
|
|||
|
|
tiered["monument"].append((gid, data))
|
|||
|
|
else:
|
|||
|
|
tiered["standard"].append((gid, data))
|
|||
|
|
|
|||
|
|
def sort_key(item: Tuple[str, Dict[str, Any]]) -> Tuple[float, str]:
|
|||
|
|
gid, data = item
|
|||
|
|
priority = float(data.get("priority", 1))
|
|||
|
|
band = data.get("band", "C")
|
|||
|
|
band_order = {"A": 0, "B": 1, "C": 2, "D": 3}.get(band, 4)
|
|||
|
|
return (-priority, band_order, gid)
|
|||
|
|
|
|||
|
|
for tier_name in tiered:
|
|||
|
|
tiered[tier_name].sort(key=sort_key)
|
|||
|
|
|
|||
|
|
regions: List[MemoryRegion] = []
|
|||
|
|
cursor = RESERVED_BASE
|
|||
|
|
|
|||
|
|
reserved_region = MemoryRegion(
|
|||
|
|
glyph_id="__reserved__",
|
|||
|
|
base=cursor,
|
|||
|
|
size=AETHER_NODE_BASE - RESERVED_BASE,
|
|||
|
|
band="",
|
|||
|
|
priority=0,
|
|||
|
|
label="System reserved",
|
|||
|
|
type="reserved",
|
|||
|
|
permissions="r",
|
|||
|
|
)
|
|||
|
|
regions.append(reserved_region)
|
|||
|
|
cursor = AETHER_NODE_BASE
|
|||
|
|
|
|||
|
|
for gid, data in tiered["aether"]:
|
|||
|
|
region = cls._allocate_region(gid, data, cursor, "aether")
|
|||
|
|
regions.append(region)
|
|||
|
|
cursor = region.base + region.size
|
|||
|
|
|
|||
|
|
cursor = max(cursor, MONUMENT_BASE)
|
|||
|
|
|
|||
|
|
for gid, data in tiered["monument"]:
|
|||
|
|
region = cls._allocate_region(gid, data, cursor, "monument")
|
|||
|
|
regions.append(region)
|
|||
|
|
cursor = region.base + region.size
|
|||
|
|
|
|||
|
|
cursor = max(cursor, STANDARD_BASE)
|
|||
|
|
|
|||
|
|
for gid, data in tiered["standard"]:
|
|||
|
|
region = cls._allocate_region(gid, data, cursor, "standard")
|
|||
|
|
regions.append(region)
|
|||
|
|
cursor = region.base + region.size
|
|||
|
|
|
|||
|
|
cursor = max(cursor, STACK_BASE)
|
|||
|
|
stack_region = MemoryRegion(
|
|||
|
|
glyph_id="__stack__",
|
|||
|
|
base=cursor,
|
|||
|
|
size=MAX_ADDRESS - cursor + 1,
|
|||
|
|
band="",
|
|||
|
|
priority=0,
|
|||
|
|
label="Execution stack",
|
|||
|
|
type="stack",
|
|||
|
|
permissions="rw",
|
|||
|
|
)
|
|||
|
|
regions.append(stack_region)
|
|||
|
|
|
|||
|
|
glyph_map: Dict[str, MemoryRegion] = {}
|
|||
|
|
for r in regions:
|
|||
|
|
if not r.glyph_id.startswith("__"):
|
|||
|
|
glyph_map[r.glyph_id] = r
|
|||
|
|
|
|||
|
|
return cls(regions=regions, glyph_map=glyph_map, total_size=MAX_ADDRESS + 1)
|
|||
|
|
|
|||
|
|
def get_offset(self, glyph_id: str) -> Optional[int]:
|
|||
|
|
"""Get the base address for a glyph.
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
glyph_id: The glyph to look up.
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
Base address as int, or None if glyph not in layout.
|
|||
|
|
"""
|
|||
|
|
region = self.glyph_map.get(glyph_id)
|
|||
|
|
if region:
|
|||
|
|
return region.base
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def get_region(self, glyph_id: str) -> Optional[MemoryRegion]:
|
|||
|
|
"""Get the full region descriptor for a glyph."""
|
|||
|
|
return self.glyph_map.get(glyph_id)
|
|||
|
|
|
|||
|
|
def get_region_for_address(self, address: int) -> Optional[MemoryRegion]:
|
|||
|
|
"""Find which region an address falls in.
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
address: 16-bit address.
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
MemoryRegion containing the address, or None.
|
|||
|
|
"""
|
|||
|
|
for region in self.regions:
|
|||
|
|
if region.base <= address < region.base + region.size:
|
|||
|
|
return region
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def map_segments(
|
|||
|
|
self,
|
|||
|
|
segments: List[Dict[str, Any]],
|
|||
|
|
) -> List[Dict[str, int]]:
|
|||
|
|
"""Map code segments to concrete addresses in the layout.
|
|||
|
|
|
|||
|
|
Each segment gets assigned to the region of its associated glyph
|
|||
|
|
(or the first available region if no glyph match).
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
segments: List of segment dicts with keys: id, glyph_id, size.
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
List of segment mappings: {segment_id, glyph_id, address, size}.
|
|||
|
|
"""
|
|||
|
|
mappings: List[Dict[str, int]] = []
|
|||
|
|
region_cursors: Dict[str, int] = {}
|
|||
|
|
|
|||
|
|
for seg in segments:
|
|||
|
|
seg_id = seg.get("id", "unknown")
|
|||
|
|
gid = seg.get("glyph_id", "")
|
|||
|
|
seg_size = seg.get("size", PAGE_SIZE)
|
|||
|
|
|
|||
|
|
region = self.glyph_map.get(gid)
|
|||
|
|
if region is None:
|
|||
|
|
region = self.regions[0] if self.regions else None
|
|||
|
|
if region is None:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if gid not in region_cursors:
|
|||
|
|
region_cursors[gid] = region.base
|
|||
|
|
cursor = region_cursors[gid]
|
|||
|
|
|
|||
|
|
max_size = region.size - (cursor - region.base)
|
|||
|
|
actual_size = min(seg_size, max_size)
|
|||
|
|
|
|||
|
|
mappings.append({
|
|||
|
|
"segment_id": seg_id,
|
|||
|
|
"glyph_id": gid,
|
|||
|
|
"address": cursor,
|
|||
|
|
"size": actual_size,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
region_cursors[gid] = cursor + actual_size
|
|||
|
|
|
|||
|
|
return mappings
|
|||
|
|
|
|||
|
|
def to_dict(self) -> Dict[str, Any]:
|
|||
|
|
"""Serialize layout to a dict for telemetry or inspection."""
|
|||
|
|
return {
|
|||
|
|
"total_size": self.total_size,
|
|||
|
|
"region_count": len(self.regions),
|
|||
|
|
"glyph_count": len(self.glyph_map),
|
|||
|
|
"regions": [
|
|||
|
|
{
|
|||
|
|
"glyph_id": r.glyph_id,
|
|||
|
|
"base": f"0x{r.base:04X}",
|
|||
|
|
"size": r.size,
|
|||
|
|
"band": r.band,
|
|||
|
|
"type": r.type,
|
|||
|
|
"permissions": r.permissions,
|
|||
|
|
"label": r.label,
|
|||
|
|
}
|
|||
|
|
for r in self.regions
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def _allocate_region(
|
|||
|
|
glyph_id: str,
|
|||
|
|
data: Dict[str, Any],
|
|||
|
|
base: int,
|
|||
|
|
tier: str,
|
|||
|
|
) -> MemoryRegion:
|
|||
|
|
"""Allocate a region for a single glyph."""
|
|||
|
|
band = data.get("band", "C") if tier != "aether" else "A"
|
|||
|
|
priority = float(data.get("priority", 1))
|
|||
|
|
score = float(data.get("score", 100))
|
|||
|
|
|
|||
|
|
band_mult = BAND_SIZE_MULTIPLIERS.get(band, 4)
|
|||
|
|
size = PAGE_SIZE * band_mult
|
|||
|
|
|
|||
|
|
if tier == "aether":
|
|||
|
|
size = PAGE_SIZE * 32
|
|||
|
|
|
|||
|
|
name = data.get("name", glyph_id)
|
|||
|
|
label = f"[{tier}] {name} ({glyph_id})"
|
|||
|
|
|
|||
|
|
return MemoryRegion(
|
|||
|
|
glyph_id=glyph_id,
|
|||
|
|
base=base,
|
|||
|
|
size=size,
|
|||
|
|
band=band,
|
|||
|
|
priority=priority,
|
|||
|
|
label=label,
|
|||
|
|
type="code",
|
|||
|
|
permissions="rx",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def _load_glyph_data(
|
|||
|
|
glyph_ids: List[str],
|
|||
|
|
) -> Dict[str, Dict[str, Any]]:
|
|||
|
|
"""Load glyph data from the super_registry."""
|
|||
|
|
try:
|
|||
|
|
from glyphs.super_registry import get_super
|
|||
|
|
result: Dict[str, Dict[str, Any]] = {}
|
|||
|
|
for gid in glyph_ids:
|
|||
|
|
glyph = get_super(gid)
|
|||
|
|
if glyph:
|
|||
|
|
result[gid] = glyph
|
|||
|
|
else:
|
|||
|
|
result[gid] = {"name": gid, "priority": 1, "band": "C", "score": 50}
|
|||
|
|
return result
|
|||
|
|
except ImportError:
|
|||
|
|
logger.warning("[GAML] super_registry not available, using defaults")
|
|||
|
|
return {}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def build_layout(
|
|||
|
|
glyph_ids: List[str],
|
|||
|
|
glyph_data: Optional[Dict[str, Any]] = None,
|
|||
|
|
) -> GlyphAlignedMemoryLayout:
|
|||
|
|
"""Convenience: build a layout for the given glyph IDs."""
|
|||
|
|
return GlyphAlignedMemoryLayout.build(glyph_ids, glyph_data)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_glyph_address(
|
|||
|
|
layout: GlyphAlignedMemoryLayout,
|
|||
|
|
glyph_id: str,
|
|||
|
|
) -> Optional[int]:
|
|||
|
|
"""Get a glyph's base address from the layout."""
|
|||
|
|
return layout.get_offset(glyph_id)
|