149 lines
5.2 KiB
Python
149 lines
5.2 KiB
Python
|
|
"""
|
||
|
|
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)})"
|
||
|
|
)
|