356 lines
11 KiB
Python
Executable File
356 lines
11 KiB
Python
Executable File
"""
|
||
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)
|