""" Function-local state container for LLVM lowering. Phase 132-P1: Box-First isolation of function-level state. Prevents cross-function state leakage and collisions. """ from typing import Dict, Set, Any from llvmlite import ir class FunctionLowerContext: """ Box containing all function-local lowering state. Lifetime: Created at function entry, destroyed at function exit. Scope: Single MIR function lowering. Benefits: - Automatic state isolation (no manual clearing needed) - Clear ownership (context belongs to one function) - Easy to test (create context, lower, verify) Design Principle (Box-First): All function-scoped state lives here. This Box is created fresh for each function and automatically destroyed when the function lowering completes, ensuring zero cross-function contamination. """ def __init__(self, func_name: str): """Initialize function-local context. Args: func_name: Name of the function being lowered (for debugging/tracing) """ self.func_name = func_name # Block snapshot state (was: builder.block_end_values with tuple-key) # Maps: block_id -> {value_id -> ir.Value} # SSOT: End-of-block value snapshots for PHI wiring self.block_end_values: Dict[int, Dict[int, ir.Value]] = {} # Definition tracking (was: builder.def_blocks) # Maps: value_id -> set of block_ids where it's defined # Used by resolver to determine if value is defined in current block self.def_blocks: Dict[int, Set[int]] = {} # Jump-only blocks (was: builder._jump_only_blocks) # Maps: jump_only_block_id -> predecessor_block_id # Used for snapshot resolution in Pass B self.jump_only_blocks: Dict[int, int] = {} # PHI management (was: builder.phi_manager) # Will be set to PhiManager instance self.phi_manager: Any = None # Type: PhiManager (avoid circular import) # Resolver caches (function-local) # These caches are keyed by (block_name, value_id) and must be # cleared between functions to prevent cross-function collisions self.resolver_i64_cache: Dict = {} self.resolver_ptr_cache: Dict = {} self.resolver_f64_cache: Dict = {} self.resolver_end_i64_cache: Dict = {} # String-related caches (function-local) self.resolver_string_ids: Set[int] = set() self.resolver_string_literals: Dict[int, str] = {} self.resolver_string_ptrs: Dict[int, ir.Value] = {} self.resolver_length_cache: Dict[int, ir.Value] = {} # NewBox→string-arg hints (function-local) self.resolver_newbox_string_args: Dict = {} # PHI incomings metadata (function-local) # Maps: block_id -> {value_id -> [(pred_bid, val_vid), ...]} self.block_phi_incomings: Dict[int, Dict[int, Any]] = {} def get_block_snapshot(self, block_id: int) -> Dict[int, ir.Value]: """Get end-of-block value snapshot for a block. Args: block_id: Block ID to get snapshot for Returns: Dictionary mapping value_id -> ir.Value (empty dict if not found) """ return self.block_end_values.get(block_id, {}) def set_block_snapshot(self, block_id: int, snapshot: Dict[int, ir.Value]) -> None: """Set end-of-block value snapshot for a block. Args: block_id: Block ID to set snapshot for snapshot: Dictionary mapping value_id -> ir.Value """ self.block_end_values[block_id] = snapshot def register_jump_only_block(self, block_id: int, pred_id: int) -> None: """Register a block as jump-only (trampoline block). Args: block_id: ID of jump-only block pred_id: ID of predecessor block to copy snapshot from """ self.jump_only_blocks[block_id] = pred_id def is_jump_only(self, block_id: int) -> bool: """Check if a block is registered as jump-only. Args: block_id: Block ID to check Returns: True if block is jump-only, False otherwise """ return block_id in self.jump_only_blocks def add_def_block(self, value_id: int, block_id: int) -> None: """Record that a value is defined in a block. Args: value_id: Value ID block_id: Block ID where value is defined """ if value_id not in self.def_blocks: self.def_blocks[value_id] = set() self.def_blocks[value_id].add(block_id) def is_defined_in_block(self, value_id: int, block_id: int) -> bool: """Check if a value is defined in a specific block. Args: value_id: Value ID to check block_id: Block ID to check Returns: True if value is defined in the block, False otherwise """ return block_id in self.def_blocks.get(value_id, set()) def __repr__(self) -> str: """String representation for debugging.""" return ( f"FunctionLowerContext(func_name={self.func_name!r}, " f"blocks={len(self.block_end_values)}, " f"jump_only={len(self.jump_only_blocks)}, " f"defs={len(self.def_blocks)})" )