Files
hakorune/src/llvm_py/resolver.py

112 lines
3.9 KiB
Python
Raw Normal View History

"""
Resolver API (Python version)
Based on src/backend/llvm/compiler/codegen/instructions/resolver.rs
"""
from typing import Dict, Optional, Any, Tuple
import llvmlite.ir as ir
class Resolver:
"""
Centralized value resolution with per-block caching.
Following the Core Invariants from LLVM_LAYER_OVERVIEW.md:
- Resolver-only reads
- Localize at block start (PHI creation)
- Cache per (block, value) to avoid redundant PHIs
"""
def __init__(self, builder: ir.IRBuilder, module: ir.Module):
self.builder = builder
self.module = module
# Caches: (block_name, value_id) -> llvm value
self.i64_cache: Dict[Tuple[str, int], ir.Value] = {}
self.ptr_cache: Dict[Tuple[str, int], ir.Value] = {}
self.f64_cache: Dict[Tuple[str, int], ir.Value] = {}
# Type shortcuts
self.i64 = ir.IntType(64)
self.i8p = ir.IntType(8).as_pointer()
self.f64_type = ir.DoubleType()
def resolve_i64(
self,
value_id: int,
current_block: ir.Block,
preds: Dict[str, list],
block_end_values: Dict[str, Dict[int, Any]],
vmap: Dict[int, Any]
) -> ir.Value:
"""
Resolve a MIR value as i64 dominating the current block.
Creates PHI at block start if needed, caches the result.
"""
cache_key = (current_block.name, value_id)
# Check cache
if cache_key in self.i64_cache:
return self.i64_cache[cache_key]
# Get predecessor blocks
pred_names = preds.get(current_block.name, [])
if not pred_names:
# Entry block or no predecessors
base_val = vmap.get(value_id, ir.Constant(self.i64, 0))
result = self._coerce_to_i64(base_val)
else:
# Create PHI at block start
saved_pos = self.builder.block
self.builder.position_at_start(current_block)
phi = self.builder.phi(self.i64, name=f"loc_i64_{value_id}")
# Add incoming values from predecessors
for pred_name in pred_names:
pred_vals = block_end_values.get(pred_name, {})
val = pred_vals.get(value_id, ir.Constant(self.i64, 0))
coerced = self._coerce_to_i64(val)
# Note: In real implementation, need pred block reference
phi.add_incoming(coerced, pred_name) # Simplified
# Restore position
if saved_pos:
self.builder.position_at_end(saved_pos)
result = phi
# Cache and return
self.i64_cache[cache_key] = result
return result
def resolve_ptr(self, value_id: int, current_block: ir.Block,
preds: Dict, block_end_values: Dict, vmap: Dict) -> ir.Value:
"""Resolve as i8* pointer"""
# Similar to resolve_i64 but with pointer type
# TODO: Implement
pass
def resolve_f64(self, value_id: int, current_block: ir.Block,
preds: Dict, block_end_values: Dict, vmap: Dict) -> ir.Value:
"""Resolve as f64"""
# Similar pattern
# TODO: Implement
pass
def _coerce_to_i64(self, val: Any) -> ir.Value:
"""Coerce various types to i64"""
if isinstance(val, ir.Constant) and val.type == self.i64:
return val
elif hasattr(val, 'type') and val.type.is_pointer:
# ptr to int
return self.builder.ptrtoint(val, self.i64)
elif hasattr(val, 'type') and isinstance(val.type, ir.IntType):
# int to int (extend/trunc)
if val.type.width < 64:
return self.builder.zext(val, self.i64)
elif val.type.width > 64:
return self.builder.trunc(val, self.i64)
return val
else:
# Default zero
return ir.Constant(self.i64, 0)