Refactor address types to use newtype wrappers
This commit introduces newtype wrappers for `LocalSlot`, `UpvalueIdx`, and `GlobalIdx` to improve type safety and clarity. These wrappers replace direct use of `u32` for indices, making the code more robust and easier to understand. The changes include: - Defining `LocalSlot`, `UpvalueIdx`, and `GlobalIdx` structs. - Implementing `Display` for these new types to provide user-friendly output. - Updating the `Address` enum to use these new types. - Modifying various compiler components (analyzer, binder, optimizer, type checker, environment, VM) to use the new types. - Adjusting tests to accommodate the changes.
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
;; Benchmark: 129ns
|
||||||
|
;; Benchmark-Repeat: 15510
|
||||||
(do
|
(do
|
||||||
(macro wrap [f] `(fn [x] (~f x)))
|
(macro wrap [f] `(fn [x] (~f x)))
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics, TypedNode};
|
use crate::ast::compiler::bound_nodes::{
|
||||||
|
Address, AnalyzedNode, BoundKind, GlobalIdx, NodeMetrics, TypedNode,
|
||||||
|
};
|
||||||
use crate::ast::types::Purity;
|
use crate::ast::types::Purity;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Analyzer<'a> {
|
pub struct Analyzer<'a> {
|
||||||
global_purity: &'a HashMap<u32, Purity>,
|
global_purity: &'a HashMap<GlobalIdx, Purity>,
|
||||||
/// Stack of currently visiting lambdas to detect direct recursion.
|
/// Stack of currently visiting lambdas to detect direct recursion.
|
||||||
lambda_stack: Vec<crate::ast::types::Identity>,
|
lambda_stack: Vec<crate::ast::types::Identity>,
|
||||||
/// Map of global index to its Lambda identity if known.
|
/// Map of global index to its Lambda identity if known.
|
||||||
globals_to_lambdas: HashMap<u32, crate::ast::types::Identity>,
|
globals_to_lambdas: HashMap<GlobalIdx, crate::ast::types::Identity>,
|
||||||
/// Set of identities that were found to be recursive.
|
/// Set of identities that were found to be recursive.
|
||||||
recursive_identities: HashSet<crate::ast::types::Identity>,
|
recursive_identities: HashSet<crate::ast::types::Identity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Analyzer<'a> {
|
impl<'a> Analyzer<'a> {
|
||||||
pub fn analyze(node: &TypedNode, global_purity: &'a HashMap<u32, Purity>) -> AnalyzedNode {
|
pub fn analyze(
|
||||||
|
node: &TypedNode,
|
||||||
|
global_purity: &'a HashMap<GlobalIdx, Purity>,
|
||||||
|
) -> AnalyzedNode {
|
||||||
let mut analyzer = Self {
|
let mut analyzer = Self {
|
||||||
global_purity,
|
global_purity,
|
||||||
lambda_stack: Vec::new(),
|
lambda_stack: Vec::new(),
|
||||||
|
|||||||
+19
-14
@@ -1,4 +1,6 @@
|
|||||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, DeclarationKind};
|
use crate::ast::compiler::bound_nodes::{
|
||||||
|
Address, BoundKind, BoundNode, DeclarationKind, GlobalIdx, LocalSlot, UpvalueIdx,
|
||||||
|
};
|
||||||
use crate::ast::nodes::{Node, Symbol, UntypedKind};
|
use crate::ast::nodes::{Node, Symbol, UntypedKind};
|
||||||
use crate::ast::types::{Identity, StaticType};
|
use crate::ast::types::{Identity, StaticType};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@@ -7,7 +9,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct LocalInfo {
|
struct LocalInfo {
|
||||||
slot: u32,
|
slot: LocalSlot,
|
||||||
identity: Identity,
|
identity: Identity,
|
||||||
// Note: Binder doesn't strictly need the type anymore,
|
// Note: Binder doesn't strictly need the type anymore,
|
||||||
// but it might be useful for built-ins during resolution.
|
// but it might be useful for built-ins during resolution.
|
||||||
@@ -29,14 +31,14 @@ impl CompilerScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define(&mut self, sym: &Symbol, identity: Identity) -> Result<u32, String> {
|
fn define(&mut self, sym: &Symbol, identity: Identity) -> Result<LocalSlot, String> {
|
||||||
if self.locals.contains_key(sym) {
|
if self.locals.contains_key(sym) {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Variable '{}' is already defined in this scope level.",
|
"Variable '{}' is already defined in this scope level.",
|
||||||
sym.name
|
sym.name
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let slot = self.slot_count;
|
let slot = LocalSlot(self.slot_count);
|
||||||
self.locals.insert(
|
self.locals.insert(
|
||||||
sym.clone(),
|
sym.clone(),
|
||||||
LocalInfo {
|
LocalInfo {
|
||||||
@@ -81,7 +83,7 @@ impl FunctionCompiler {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: &Symbol,
|
name: &Symbol,
|
||||||
identity: Identity,
|
identity: Identity,
|
||||||
globals: &Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
globals: &Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
) -> Result<Address, String> {
|
) -> Result<Address, String> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ScopeKind::Root => {
|
ScopeKind::Root => {
|
||||||
@@ -93,8 +95,8 @@ impl FunctionCompiler {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
let idx = globals_map.len() as u32;
|
let idx = globals_map.len() as u32;
|
||||||
globals_map.insert(name.clone(), (idx, identity));
|
globals_map.insert(name.clone(), (GlobalIdx(idx), identity));
|
||||||
Ok(Address::Global(idx))
|
Ok(Address::Global(GlobalIdx(idx)))
|
||||||
}
|
}
|
||||||
ScopeKind::Local => {
|
ScopeKind::Local => {
|
||||||
let slot = self.scope.define(name, identity)?;
|
let slot = self.scope.define(name, identity)?;
|
||||||
@@ -103,11 +105,11 @@ impl FunctionCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_upvalue(&mut self, addr: Address) -> u32 {
|
fn add_upvalue(&mut self, addr: Address) -> UpvalueIdx {
|
||||||
if let Some(idx) = self.upvalues.iter().position(|&a| a == addr) {
|
if let Some(idx) = self.upvalues.iter().position(|&a| a == addr) {
|
||||||
return idx as u32;
|
return UpvalueIdx(idx as u32);
|
||||||
}
|
}
|
||||||
let idx = self.upvalues.len() as u32;
|
let idx = UpvalueIdx(self.upvalues.len() as u32);
|
||||||
self.upvalues.push(addr);
|
self.upvalues.push(addr);
|
||||||
idx
|
idx
|
||||||
}
|
}
|
||||||
@@ -116,13 +118,13 @@ impl FunctionCompiler {
|
|||||||
pub struct Binder {
|
pub struct Binder {
|
||||||
functions: Vec<FunctionCompiler>,
|
functions: Vec<FunctionCompiler>,
|
||||||
// Globals mapping: Symbol -> (Index, DefinitionIdentity)
|
// Globals mapping: Symbol -> (Index, DefinitionIdentity)
|
||||||
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
// Map of Declaration Identity -> List of Lambda Identities that capture it
|
// Map of Declaration Identity -> List of Lambda Identities that capture it
|
||||||
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
|
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Binder {
|
impl Binder {
|
||||||
pub fn new(globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>) -> Self {
|
pub fn new(globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>) -> Self {
|
||||||
let mut binder = Self {
|
let mut binder = Self {
|
||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
globals,
|
globals,
|
||||||
@@ -130,13 +132,16 @@ impl Binder {
|
|||||||
};
|
};
|
||||||
binder.functions.push(FunctionCompiler::new(
|
binder.functions.push(FunctionCompiler::new(
|
||||||
ScopeKind::Root,
|
ScopeKind::Root,
|
||||||
crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0 }),
|
crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation {
|
||||||
|
line: 0,
|
||||||
|
col: 0,
|
||||||
|
}),
|
||||||
));
|
));
|
||||||
binder
|
binder
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_root(
|
pub fn bind_root(
|
||||||
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
node: &Node<UntypedKind>,
|
node: &Node<UntypedKind>,
|
||||||
) -> Result<(BoundNode, HashMap<Identity, Vec<Identity>>), String> {
|
) -> Result<(BoundNode, HashMap<Identity, Vec<Identity>>), String> {
|
||||||
let mut binder = Self::new(globals);
|
let mut binder = Self::new(globals);
|
||||||
|
|||||||
@@ -2,11 +2,38 @@ use crate::ast::nodes::{Node, Symbol};
|
|||||||
use crate::ast::types::{Identity, StaticType, Value};
|
use crate::ast::types::{Identity, StaticType, Value};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct LocalSlot(pub u32);
|
||||||
|
|
||||||
|
impl std::fmt::Display for LocalSlot {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "L{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct UpvalueIdx(pub u32);
|
||||||
|
|
||||||
|
impl std::fmt::Display for UpvalueIdx {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "U{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct GlobalIdx(pub u32);
|
||||||
|
|
||||||
|
impl std::fmt::Display for GlobalIdx {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "G{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum Address {
|
pub enum Address {
|
||||||
Local(u32), // Stack-Slot index (relative to frame base)
|
Local(LocalSlot), // Stack-Slot index (relative to frame base)
|
||||||
Upvalue(u32), // Index in the closure's upvalue array
|
Upvalue(UpvalueIdx), // Index in the closure's upvalue array
|
||||||
Global(u32), // Index in the global environment vector
|
Global(GlobalIdx), // Index in the global environment vector
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|||||||
@@ -101,7 +101,9 @@ impl Dumper {
|
|||||||
if !captured_by.is_empty() {
|
if !captured_by.is_empty() {
|
||||||
for capturer in captured_by {
|
for capturer in captured_by {
|
||||||
self.write_indent();
|
self.write_indent();
|
||||||
let loc = capturer.location.unwrap_or(crate::ast::types::SourceLocation { line: 0, col: 0 });
|
let loc = capturer
|
||||||
|
.location
|
||||||
|
.unwrap_or(crate::ast::types::SourceLocation { line: 0, col: 0 });
|
||||||
self.output.push_str(&format!(
|
self.output.push_str(&format!(
|
||||||
"- Capturer: Lambda at line {}, col {}\n",
|
"- Capturer: Lambda at line {}, col {}\n",
|
||||||
loc.line, loc.col
|
loc.line, loc.col
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode};
|
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, GlobalIdx};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// A pass that collects all global function definitions (lambdas) into a registry.
|
/// A pass that collects all global function definitions (lambdas) into a registry.
|
||||||
/// This allows the Specializer to retrieve the original AST of a function for monomorphization.
|
/// This allows the Specializer to retrieve the original AST of a function for monomorphization.
|
||||||
pub struct LambdaCollector<'a, T> {
|
pub struct LambdaCollector<'a, T> {
|
||||||
registry: &'a mut HashMap<u32, BoundNode<T>>,
|
registry: &'a mut HashMap<GlobalIdx, BoundNode<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Clone> LambdaCollector<'a, T> {
|
impl<'a, T: Clone> LambdaCollector<'a, T> {
|
||||||
/// Performs a full traversal of the AST and populates the provided registry.
|
/// Performs a full traversal of the AST and populates the provided registry.
|
||||||
pub fn collect(node: &BoundNode<T>, registry: &'a mut HashMap<u32, BoundNode<T>>) {
|
pub fn collect(node: &BoundNode<T>, registry: &'a mut HashMap<GlobalIdx, BoundNode<T>>) {
|
||||||
let mut collector = Self { registry };
|
let mut collector = Self { registry };
|
||||||
collector.visit(node);
|
collector.visit(node);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -716,8 +716,11 @@ mod tests {
|
|||||||
global_names.insert(
|
global_names.insert(
|
||||||
Symbol::from("*"),
|
Symbol::from("*"),
|
||||||
(
|
(
|
||||||
0,
|
crate::ast::compiler::bound_nodes::GlobalIdx(0),
|
||||||
crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0 }),
|
crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation {
|
||||||
|
line: 0,
|
||||||
|
col: 0,
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let globals = Rc::new(RefCell::new(global_names));
|
let globals = Rc::new(RefCell::new(global_names));
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics};
|
use crate::ast::compiler::bound_nodes::{
|
||||||
|
Address, AnalyzedNode, BoundKind, GlobalIdx, LocalSlot, NodeMetrics, UpvalueIdx,
|
||||||
|
};
|
||||||
use crate::ast::nodes::Node;
|
use crate::ast::nodes::Node;
|
||||||
use crate::ast::types::{Purity, StaticType, Value};
|
use crate::ast::types::{Purity, StaticType, Value};
|
||||||
use crate::ast::vm::Closure;
|
use crate::ast::vm::Closure;
|
||||||
@@ -10,13 +12,13 @@ pub struct Optimizer {
|
|||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
max_passes: usize,
|
max_passes: usize,
|
||||||
pub globals: Option<Rc<RefCell<Vec<Value>>>>,
|
pub globals: Option<Rc<RefCell<Vec<Value>>>>,
|
||||||
pub global_purity: Option<Rc<RefCell<HashMap<u32, Purity>>>>,
|
pub global_purity: Option<Rc<RefCell<HashMap<GlobalIdx, Purity>>>>,
|
||||||
pub lambda_registry: Option<Rc<RefCell<HashMap<u32, AnalyzedNode>>>>,
|
pub lambda_registry: Option<Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PathTracker {
|
struct PathTracker {
|
||||||
inlining_depth: usize,
|
inlining_depth: usize,
|
||||||
inlining_stack: HashSet<u32>,
|
inlining_stack: HashSet<GlobalIdx>,
|
||||||
identity_stack: HashSet<crate::ast::types::Identity>,
|
identity_stack: HashSet<crate::ast::types::Identity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,12 +77,15 @@ impl Optimizer {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_purity(mut self, purity: Rc<RefCell<HashMap<u32, Purity>>>) -> Self {
|
pub fn with_purity(mut self, purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>) -> Self {
|
||||||
self.global_purity = Some(purity);
|
self.global_purity = Some(purity);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_registry(mut self, registry: Rc<RefCell<HashMap<u32, AnalyzedNode>>>) -> Self {
|
pub fn with_registry(
|
||||||
|
mut self,
|
||||||
|
registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
|
||||||
|
) -> Self {
|
||||||
self.lambda_registry = Some(registry);
|
self.lambda_registry = Some(registry);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -146,7 +151,7 @@ impl Optimizer {
|
|||||||
&& let Some(globals_rc) = &self.globals
|
&& let Some(globals_rc) = &self.globals
|
||||||
{
|
{
|
||||||
let globals = globals_rc.borrow();
|
let globals = globals_rc.borrow();
|
||||||
if let Some(val) = globals.get(idx as usize)
|
if let Some(val) = globals.get(idx.0 as usize)
|
||||||
&& self.is_inlinable_value(val, addr)
|
&& self.is_inlinable_value(val, addr)
|
||||||
{
|
{
|
||||||
return self.make_constant_node(val.clone(), &node);
|
return self.make_constant_node(val.clone(), &node);
|
||||||
@@ -304,8 +309,10 @@ impl Optimizer {
|
|||||||
{
|
{
|
||||||
let mut closure_sub = SubstitutionMap::new();
|
let mut closure_sub = SubstitutionMap::new();
|
||||||
for (i, cell) in closure.upvalues.iter().enumerate() {
|
for (i, cell) in closure.upvalues.iter().enumerate() {
|
||||||
closure_sub
|
closure_sub.add_value(
|
||||||
.add_value(Address::Upvalue(i as u32), cell.borrow().clone());
|
Address::Upvalue(UpvalueIdx(i as u32)),
|
||||||
|
cell.borrow().clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
path.inlining_depth += 1;
|
path.inlining_depth += 1;
|
||||||
@@ -480,7 +487,8 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(val) = inlined_val {
|
if let Some(val) = inlined_val {
|
||||||
next_inner_subs.add_value(Address::Upvalue(old_idx as u32), val);
|
next_inner_subs
|
||||||
|
.add_value(Address::Upvalue(UpvalueIdx(old_idx as u32)), val);
|
||||||
mapping.push(None);
|
mapping.push(None);
|
||||||
} else {
|
} else {
|
||||||
mapping.push(Some(new_upvalues.len() as u32));
|
mapping.push(Some(new_upvalues.len() as u32));
|
||||||
@@ -579,7 +587,7 @@ impl Optimizer {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
sub.slot_mapping.insert(*slot, *slot);
|
sub.slot_mapping.insert(*slot, *slot);
|
||||||
sub.next_slot = sub.next_slot.max(*slot + 1);
|
sub.next_slot = sub.next_slot.max(slot.0 + 1);
|
||||||
}
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
for el in elements {
|
for el in elements {
|
||||||
@@ -684,7 +692,7 @@ impl Optimizer {
|
|||||||
BoundKind::Get {
|
BoundKind::Get {
|
||||||
addr: Address::Global(idx),
|
addr: Address::Global(idx),
|
||||||
..
|
..
|
||||||
} => self.globals.as_ref()?.borrow().get(*idx as usize)?.clone(),
|
} => self.globals.as_ref()?.borrow().get(idx.0 as usize)?.clone(),
|
||||||
BoundKind::Constant(val) => val.clone(),
|
BoundKind::Constant(val) => val.clone(),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
@@ -737,7 +745,7 @@ impl Optimizer {
|
|||||||
Some(self.visit_node(body, sub, path))
|
Some(self.visit_node(body, sub, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<u32>) {
|
fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<LocalSlot>) {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
BoundKind::Define {
|
BoundKind::Define {
|
||||||
addr: Address::Local(slot),
|
addr: Address::Local(slot),
|
||||||
@@ -802,7 +810,7 @@ impl Optimizer {
|
|||||||
// Ensure local slots are mapped even if no constant/AST is substituted
|
// Ensure local slots are mapped even if no constant/AST is substituted
|
||||||
if let Address::Local(slot) = addr {
|
if let Address::Local(slot) = addr {
|
||||||
sub.slot_mapping.insert(*slot, *slot);
|
sub.slot_mapping.insert(*slot, *slot);
|
||||||
sub.next_slot = sub.next_slot.max(*slot + 1);
|
sub.next_slot = sub.next_slot.max(slot.0 + 1);
|
||||||
}
|
}
|
||||||
*offset += 1;
|
*offset += 1;
|
||||||
}
|
}
|
||||||
@@ -895,7 +903,7 @@ impl Optimizer {
|
|||||||
// Map used upvalues to parent scope
|
// Map used upvalues to parent scope
|
||||||
for addr in &inner_info.used {
|
for addr in &inner_info.used {
|
||||||
if let Address::Upvalue(idx) = addr
|
if let Address::Upvalue(idx) = addr
|
||||||
&& let Some(parent_addr) = upvalues.get(*idx as usize)
|
&& let Some(parent_addr) = upvalues.get(idx.0 as usize)
|
||||||
{
|
{
|
||||||
info.used.insert(*parent_addr);
|
info.used.insert(*parent_addr);
|
||||||
}
|
}
|
||||||
@@ -903,7 +911,7 @@ impl Optimizer {
|
|||||||
// Map assigned upvalues to parent scope
|
// Map assigned upvalues to parent scope
|
||||||
for addr in &inner_info.assigned {
|
for addr in &inner_info.assigned {
|
||||||
if let Address::Upvalue(idx) = addr
|
if let Address::Upvalue(idx) = addr
|
||||||
&& let Some(parent_addr) = upvalues.get(*idx as usize)
|
&& let Some(parent_addr) = upvalues.get(idx.0 as usize)
|
||||||
{
|
{
|
||||||
info.assigned.insert(*parent_addr);
|
info.assigned.insert(*parent_addr);
|
||||||
}
|
}
|
||||||
@@ -954,11 +962,11 @@ impl Optimizer {
|
|||||||
struct SubstitutionMap {
|
struct SubstitutionMap {
|
||||||
values: HashMap<Address, Value>,
|
values: HashMap<Address, Value>,
|
||||||
ast_substitutions: HashMap<Address, AnalyzedNode>,
|
ast_substitutions: HashMap<Address, AnalyzedNode>,
|
||||||
slot_mapping: HashMap<u32, u32>,
|
slot_mapping: HashMap<LocalSlot, LocalSlot>,
|
||||||
assigned: HashSet<Address>,
|
assigned: HashSet<Address>,
|
||||||
next_slot: u32,
|
next_slot: u32,
|
||||||
used: HashSet<Address>,
|
used: HashSet<Address>,
|
||||||
captured_slots: HashSet<u32>,
|
captured_slots: HashSet<LocalSlot>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubstitutionMap {
|
impl SubstitutionMap {
|
||||||
@@ -978,11 +986,11 @@ impl SubstitutionMap {
|
|||||||
self.ast_substitutions.insert(addr, node);
|
self.ast_substitutions.insert(addr, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_slot(&mut self, old_slot: u32) -> u32 {
|
fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot {
|
||||||
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
|
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
|
||||||
return new_slot;
|
return new_slot;
|
||||||
}
|
}
|
||||||
let new_slot = self.next_slot;
|
let new_slot = LocalSlot(self.next_slot);
|
||||||
self.slot_mapping.insert(old_slot, new_slot);
|
self.slot_mapping.insert(old_slot, new_slot);
|
||||||
self.next_slot += 1;
|
self.next_slot += 1;
|
||||||
new_slot
|
new_slot
|
||||||
@@ -1009,10 +1017,10 @@ impl SubstitutionMap {
|
|||||||
|
|
||||||
fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address {
|
fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address {
|
||||||
if let Address::Upvalue(idx) = addr
|
if let Address::Upvalue(idx) = addr
|
||||||
&& let Some(res) = mapping.get(idx as usize)
|
&& let Some(res) = mapping.get(idx.0 as usize)
|
||||||
&& let Some(new_idx) = res
|
&& let Some(new_idx) = res
|
||||||
{
|
{
|
||||||
Address::Upvalue(*new_idx)
|
Address::Upvalue(UpvalueIdx(*new_idx))
|
||||||
} else {
|
} else {
|
||||||
addr
|
addr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, TypedNode};
|
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, GlobalIdx, TypedNode};
|
||||||
use crate::ast::nodes::Node;
|
use crate::ast::nodes::Node;
|
||||||
use crate::ast::types::StaticType;
|
use crate::ast::types::StaticType;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -12,7 +12,7 @@ struct TypeContext<'a> {
|
|||||||
/// Types of captured variables (passed from outer scope)
|
/// Types of captured variables (passed from outer scope)
|
||||||
upvalue_types: Vec<StaticType>,
|
upvalue_types: Vec<StaticType>,
|
||||||
/// Access to global types for unified resolution
|
/// Access to global types for unified resolution
|
||||||
global_types: &'a std::cell::RefCell<HashMap<u32, StaticType>>,
|
global_types: &'a std::cell::RefCell<HashMap<GlobalIdx, StaticType>>,
|
||||||
/// The expected parameters of the current function (for 'again' validation)
|
/// The expected parameters of the current function (for 'again' validation)
|
||||||
current_params_ty: Option<StaticType>,
|
current_params_ty: Option<StaticType>,
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ impl<'a> TypeContext<'a> {
|
|||||||
fn new(
|
fn new(
|
||||||
slot_count: u32,
|
slot_count: u32,
|
||||||
upvalue_types: Vec<StaticType>,
|
upvalue_types: Vec<StaticType>,
|
||||||
global_types: &'a std::cell::RefCell<HashMap<u32, StaticType>>,
|
global_types: &'a std::cell::RefCell<HashMap<GlobalIdx, StaticType>>,
|
||||||
parent: Option<&'a TypeContext<'a>>,
|
parent: Option<&'a TypeContext<'a>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -35,9 +35,9 @@ impl<'a> TypeContext<'a> {
|
|||||||
|
|
||||||
fn get_type(&self, addr: Address) -> StaticType {
|
fn get_type(&self, addr: Address) -> StaticType {
|
||||||
match addr {
|
match addr {
|
||||||
Address::Local(idx) => self
|
Address::Local(slot) => self
|
||||||
.slots
|
.slots
|
||||||
.get(idx as usize)
|
.get(slot.0 as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(StaticType::Any),
|
.unwrap_or(StaticType::Any),
|
||||||
Address::Global(idx) => self
|
Address::Global(idx) => self
|
||||||
@@ -48,7 +48,7 @@ impl<'a> TypeContext<'a> {
|
|||||||
.unwrap_or(StaticType::Any),
|
.unwrap_or(StaticType::Any),
|
||||||
Address::Upvalue(idx) => self
|
Address::Upvalue(idx) => self
|
||||||
.upvalue_types
|
.upvalue_types
|
||||||
.get(idx as usize)
|
.get(idx.0 as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(StaticType::Any),
|
.unwrap_or(StaticType::Any),
|
||||||
}
|
}
|
||||||
@@ -56,9 +56,9 @@ impl<'a> TypeContext<'a> {
|
|||||||
|
|
||||||
fn set_type(&mut self, addr: Address, ty: StaticType) {
|
fn set_type(&mut self, addr: Address, ty: StaticType) {
|
||||||
match addr {
|
match addr {
|
||||||
Address::Local(idx) => {
|
Address::Local(slot) => {
|
||||||
if let Some(slot) = self.slots.get_mut(idx as usize) {
|
if let Some(entry) = self.slots.get_mut(slot.0 as usize) {
|
||||||
*slot = ty;
|
*entry = ty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
@@ -70,11 +70,11 @@ impl<'a> TypeContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct TypeChecker {
|
pub struct TypeChecker {
|
||||||
global_types: Rc<std::cell::RefCell<HashMap<u32, StaticType>>>,
|
global_types: Rc<std::cell::RefCell<HashMap<GlobalIdx, StaticType>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeChecker {
|
impl TypeChecker {
|
||||||
pub fn new(global_types: Rc<std::cell::RefCell<HashMap<u32, StaticType>>>) -> Self {
|
pub fn new(global_types: Rc<std::cell::RefCell<HashMap<GlobalIdx, StaticType>>>) -> Self {
|
||||||
Self { global_types }
|
Self { global_types }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -8,7 +8,7 @@ use std::cell::RefCell;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, BoundNode};
|
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, BoundNode, GlobalIdx};
|
||||||
use crate::ast::compiler::dumper::Dumper;
|
use crate::ast::compiler::dumper::Dumper;
|
||||||
use crate::ast::compiler::lambda_collector::LambdaCollector;
|
use crate::ast::compiler::lambda_collector::LambdaCollector;
|
||||||
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
|
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
|
||||||
@@ -22,21 +22,21 @@ use crate::ast::types::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
pub global_names: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
pub global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
pub global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
|
pub global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
|
||||||
pub global_purity: Rc<RefCell<HashMap<u32, Purity>>>,
|
pub global_purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>,
|
||||||
pub global_values: Rc<RefCell<Vec<Value>>>,
|
pub global_values: Rc<RefCell<Vec<Value>>>,
|
||||||
pub prng: Rc<RefCell<fastrand::Rng>>,
|
pub prng: Rc<RefCell<fastrand::Rng>>,
|
||||||
pub function_registry: Rc<RefCell<HashMap<u32, BoundNode>>>,
|
pub function_registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
|
||||||
pub typed_function_registry: Rc<RefCell<HashMap<u32, AnalyzedNode>>>,
|
pub typed_function_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
|
||||||
pub monomorph_cache: Rc<RefCell<MonoCache>>,
|
pub monomorph_cache: Rc<RefCell<MonoCache>>,
|
||||||
pub debug_mode: bool,
|
pub debug_mode: bool,
|
||||||
pub optimization: bool,
|
pub optimization: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EnvFunctionRegistry {
|
struct EnvFunctionRegistry {
|
||||||
registry: Rc<RefCell<HashMap<u32, BoundNode>>>,
|
registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
|
||||||
analyzed_registry: Rc<RefCell<HashMap<u32, AnalyzedNode>>>,
|
analyzed_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionRegistry for EnvFunctionRegistry {
|
impl FunctionRegistry for EnvFunctionRegistry {
|
||||||
@@ -57,8 +57,8 @@ impl FunctionRegistry for EnvFunctionRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct RuntimeMacroEvaluator {
|
struct RuntimeMacroEvaluator {
|
||||||
global_names: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
|
global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
|
||||||
global_values: Rc<RefCell<Vec<Value>>>,
|
global_values: Rc<RefCell<Vec<Value>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ impl Environment {
|
|||||||
let mut values = self.global_values.borrow_mut();
|
let mut values = self.global_values.borrow_mut();
|
||||||
let mut purity = self.global_purity.borrow_mut();
|
let mut purity = self.global_purity.borrow_mut();
|
||||||
|
|
||||||
let idx = values.len() as u32;
|
let idx = GlobalIdx(values.len() as u32);
|
||||||
let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 });
|
let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 });
|
||||||
names.insert(Symbol::from(name), (idx, identity));
|
names.insert(Symbol::from(name), (idx, identity));
|
||||||
types.insert(idx, ty);
|
types.insert(idx, ty);
|
||||||
@@ -164,7 +164,7 @@ impl Environment {
|
|||||||
let mut values = self.global_values.borrow_mut();
|
let mut values = self.global_values.borrow_mut();
|
||||||
let mut purity = self.global_purity.borrow_mut();
|
let mut purity = self.global_purity.borrow_mut();
|
||||||
|
|
||||||
let idx = values.len() as u32;
|
let idx = GlobalIdx(values.len() as u32);
|
||||||
let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 });
|
let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 });
|
||||||
names.insert(Symbol::from(name), (idx, identity));
|
names.insert(Symbol::from(name), (idx, identity));
|
||||||
types.insert(idx, ty);
|
types.insert(idx, ty);
|
||||||
|
|||||||
+25
-25
@@ -506,9 +506,9 @@ impl VM {
|
|||||||
|
|
||||||
fn capture_upvalue(&mut self, addr: Address) -> Result<Rc<RefCell<Value>>, String> {
|
fn capture_upvalue(&mut self, addr: Address) -> Result<Rc<RefCell<Value>>, String> {
|
||||||
match addr {
|
match addr {
|
||||||
Address::Local(idx) => {
|
Address::Local(slot) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
let abs_index = frame.stack_base + (idx as usize);
|
let abs_index = frame.stack_base + (slot.0 as usize);
|
||||||
if abs_index < self.stack.len() {
|
if abs_index < self.stack.len() {
|
||||||
if let Value::Cell(cell) = &self.stack[abs_index] {
|
if let Value::Cell(cell) = &self.stack[abs_index] {
|
||||||
Ok(cell.clone())
|
Ok(cell.clone())
|
||||||
@@ -519,15 +519,15 @@ impl VM {
|
|||||||
Ok(cell)
|
Ok(cell)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Stack underflow capture local {}", idx))
|
Err(format!("Stack underflow capture local {}", slot))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Address::Upvalue(idx) => {
|
Address::Upvalue(idx) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
if let Some(closure) = &frame.closure {
|
if let Some(closure) = &frame.closure {
|
||||||
let idx = idx as usize;
|
let u_idx = idx.0 as usize;
|
||||||
if idx < closure.upvalues.len() {
|
if u_idx < closure.upvalues.len() {
|
||||||
Ok(closure.upvalues[idx].clone())
|
Ok(closure.upvalues[u_idx].clone())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Upvalue access out of bounds capture {}", idx))
|
Err(format!("Upvalue access out of bounds capture {}", idx))
|
||||||
}
|
}
|
||||||
@@ -541,23 +541,23 @@ impl VM {
|
|||||||
|
|
||||||
fn get_value(&self, addr: Address) -> Result<Value, String> {
|
fn get_value(&self, addr: Address) -> Result<Value, String> {
|
||||||
match addr {
|
match addr {
|
||||||
Address::Local(idx) => {
|
Address::Local(slot) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
let abs_index = frame.stack_base + (idx as usize);
|
let abs_index = frame.stack_base + (slot.0 as usize);
|
||||||
if abs_index < self.stack.len() {
|
if abs_index < self.stack.len() {
|
||||||
match &self.stack[abs_index] {
|
match &self.stack[abs_index] {
|
||||||
Value::Cell(cell) => Ok(cell.borrow().clone()),
|
Value::Cell(cell) => Ok(cell.borrow().clone()),
|
||||||
val => Ok(val.clone()),
|
val => Ok(val.clone()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Stack underflow access local {}", idx))
|
Err(format!("Stack underflow access local {}", slot))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
let idx = idx as usize;
|
let g_idx = idx.0 as usize;
|
||||||
let globals = self.globals.borrow();
|
let globals = self.globals.borrow();
|
||||||
if idx < globals.len() {
|
if g_idx < globals.len() {
|
||||||
Ok(globals[idx].clone())
|
Ok(globals[g_idx].clone())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Global access out of bounds {}", idx))
|
Err(format!("Global access out of bounds {}", idx))
|
||||||
}
|
}
|
||||||
@@ -565,9 +565,9 @@ impl VM {
|
|||||||
Address::Upvalue(idx) => {
|
Address::Upvalue(idx) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
if let Some(closure) = &frame.closure {
|
if let Some(closure) = &frame.closure {
|
||||||
let idx = idx as usize;
|
let u_idx = idx.0 as usize;
|
||||||
if idx < closure.upvalues.len() {
|
if u_idx < closure.upvalues.len() {
|
||||||
Ok(closure.upvalues[idx].borrow().clone())
|
Ok(closure.upvalues[u_idx].borrow().clone())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Upvalue access out of bounds {}", idx))
|
Err(format!("Upvalue access out of bounds {}", idx))
|
||||||
}
|
}
|
||||||
@@ -580,9 +580,9 @@ impl VM {
|
|||||||
|
|
||||||
fn set_value(&mut self, addr: Address, value: Value) -> Result<(), String> {
|
fn set_value(&mut self, addr: Address, value: Value) -> Result<(), String> {
|
||||||
match addr {
|
match addr {
|
||||||
Address::Local(idx) => {
|
Address::Local(slot) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
let abs_index = frame.stack_base + (idx as usize);
|
let abs_index = frame.stack_base + (slot.0 as usize);
|
||||||
if abs_index < self.stack.len() {
|
if abs_index < self.stack.len() {
|
||||||
if let Value::Cell(cell) = &self.stack[abs_index] {
|
if let Value::Cell(cell) = &self.stack[abs_index] {
|
||||||
*cell.borrow_mut() = value;
|
*cell.borrow_mut() = value;
|
||||||
@@ -592,25 +592,25 @@ impl VM {
|
|||||||
} else if abs_index == self.stack.len() {
|
} else if abs_index == self.stack.len() {
|
||||||
self.stack.push(value);
|
self.stack.push(value);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Stack gap write local {}", idx));
|
return Err(format!("Stack gap write local {}", slot));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
let idx = idx as usize;
|
let g_idx = idx.0 as usize;
|
||||||
let mut globals = self.globals.borrow_mut();
|
let mut globals = self.globals.borrow_mut();
|
||||||
if idx >= globals.len() {
|
if g_idx >= globals.len() {
|
||||||
globals.resize(idx + 1, Value::Void);
|
globals.resize(g_idx + 1, Value::Void);
|
||||||
}
|
}
|
||||||
globals[idx] = value;
|
globals[g_idx] = value;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Address::Upvalue(idx) => {
|
Address::Upvalue(idx) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
if let Some(closure) = &frame.closure {
|
if let Some(closure) = &frame.closure {
|
||||||
let idx = idx as usize;
|
let u_idx = idx.0 as usize;
|
||||||
if idx < closure.upvalues.len() {
|
if u_idx < closure.upvalues.len() {
|
||||||
*closure.upvalues[idx].borrow_mut() = value;
|
*closure.upvalues[u_idx].borrow_mut() = value;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Upvalue assignment out of bounds {}", idx))
|
Err(format!("Upvalue assignment out of bounds {}", idx))
|
||||||
|
|||||||
@@ -431,6 +431,9 @@ mod tests {
|
|||||||
dump
|
dump
|
||||||
);
|
);
|
||||||
// The definitions add1, add2, w1, w2 should be gone after dead code elimination
|
// The definitions add1, add2, w1, w2 should be gone after dead code elimination
|
||||||
assert!(!dump.contains("Define Variable"), "Definitions should be removed by DCE");
|
assert!(
|
||||||
|
!dump.contains("Define Variable"),
|
||||||
|
"Definitions should be removed by DCE"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user