bf86c76bb6
This commit refactors the purity analysis from using a `HashMap<GlobalIdx, Purity>` to a `Vec<Purity>` indexed by `GlobalIdx.0`. This change simplifies the data structure and makes purity lookups more efficient. The `Binder`'s `globals` field has also been removed as it was not being used.
179 lines
5.5 KiB
Rust
179 lines
5.5 KiB
Rust
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot};
|
|
use crate::ast::types::{Purity, Value};
|
|
use crate::ast::vm::Closure;
|
|
use std::cell::RefCell;
|
|
use std::collections::HashSet;
|
|
use std::rc::Rc;
|
|
|
|
use super::substitution_map::SubstitutionMap;
|
|
use super::utils::UsageInfo;
|
|
|
|
pub struct Inliner<'a> {
|
|
pub globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
|
pub root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
|
|
}
|
|
|
|
impl<'a> Inliner<'a> {
|
|
pub fn new(
|
|
globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
|
root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
|
|
) -> Self {
|
|
Self {
|
|
globals,
|
|
root_purity,
|
|
}
|
|
}
|
|
|
|
pub fn is_inlinable_value(&self, val: &Value, addr: Address) -> bool {
|
|
let type_ok = match val {
|
|
Value::Int(_)
|
|
| Value::Float(_)
|
|
| Value::Bool(_)
|
|
| Value::Text(_)
|
|
| Value::Keyword(_)
|
|
| Value::Record(_, _)
|
|
| Value::DateTime(_) => true,
|
|
Value::Object(obj) => {
|
|
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
|
closure.upvalues.is_empty() && !closure.function_node.ty.is_recursive
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
_ => false,
|
|
};
|
|
|
|
if !type_ok {
|
|
return false;
|
|
}
|
|
|
|
if let Address::Global(idx) = addr {
|
|
if let Some(purity_rc) = &self.root_purity {
|
|
let purity = purity_rc
|
|
.borrow()
|
|
.get(idx.0 as usize)
|
|
.cloned()
|
|
.unwrap_or(Purity::Impure);
|
|
return purity >= Purity::Pure;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
pub fn prepare_beta_reduction(
|
|
&self,
|
|
params: &AnalyzedNode,
|
|
arg_vals: &[Rc<AnalyzedNode>],
|
|
body: &AnalyzedNode,
|
|
sub: &mut SubstitutionMap,
|
|
) -> Option<()> {
|
|
let mut body_usage = UsageInfo::default();
|
|
body_usage.collect(body);
|
|
|
|
let mut slot_index = 0;
|
|
self.map_params_to_args(params, arg_vals, &mut slot_index, sub, &body_usage);
|
|
|
|
if slot_index != arg_vals.len() {
|
|
return None;
|
|
}
|
|
|
|
let mut param_slots = HashSet::new();
|
|
self.collect_parameter_slots_set(params, &mut param_slots);
|
|
|
|
for slot in param_slots {
|
|
let addr = Address::Local(slot);
|
|
if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr))
|
|
&& sub.get_value(&addr).is_none()
|
|
&& !sub.ast_substitutions.contains_key(&addr)
|
|
{
|
|
return None;
|
|
}
|
|
}
|
|
|
|
if sub.values.is_empty() && sub.ast_substitutions.is_empty() && !arg_vals.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
Some(())
|
|
}
|
|
|
|
pub fn map_params_to_args(
|
|
&self,
|
|
pattern: &AnalyzedNode,
|
|
args: &[Rc<AnalyzedNode>],
|
|
offset: &mut usize,
|
|
sub: &mut SubstitutionMap,
|
|
body_usage: &UsageInfo,
|
|
) {
|
|
match &pattern.kind {
|
|
BoundKind::Define { addr, .. } => {
|
|
if let Some(arg) = args.get(*offset)
|
|
&& !body_usage.is_assigned(addr)
|
|
{
|
|
if let BoundKind::Constant(val) = &arg.kind {
|
|
sub.add_value(*addr, val.clone());
|
|
} else if let BoundKind::Lambda { upvalues, .. } = &arg.kind
|
|
&& upvalues.is_empty()
|
|
{
|
|
sub.add_ast_substitution(*addr, (**arg).clone());
|
|
} else if let BoundKind::Get {
|
|
addr: Address::Global(_),
|
|
..
|
|
} = &arg.kind
|
|
{
|
|
sub.add_ast_substitution(*addr, (**arg).clone());
|
|
}
|
|
}
|
|
|
|
if let Address::Local(slot) = addr {
|
|
sub.map_slot(*slot);
|
|
}
|
|
*offset += 1;
|
|
}
|
|
BoundKind::Tuple { elements } => {
|
|
for el in elements {
|
|
self.map_params_to_args(el, args, offset, sub, body_usage);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<LocalSlot>) {
|
|
match &node.kind {
|
|
BoundKind::Define {
|
|
addr: Address::Local(slot),
|
|
..
|
|
} => {
|
|
slots.insert(*slot);
|
|
}
|
|
BoundKind::Tuple { elements } => {
|
|
for el in elements {
|
|
self.collect_parameter_slots_set(el, slots);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
pub fn collect_parameter_slots(&self, node: &AnalyzedNode, sub: &mut SubstitutionMap) {
|
|
match &node.kind {
|
|
BoundKind::Define {
|
|
addr: Address::Local(slot),
|
|
..
|
|
} => {
|
|
sub.slot_mapping.insert(*slot, *slot);
|
|
sub.next_slot = sub.next_slot.max(slot.0 + 1);
|
|
}
|
|
BoundKind::Tuple { elements } => {
|
|
for el in elements {
|
|
self.collect_parameter_slots(el, sub);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|