Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ parking_lot = "0.12.0"
colorful = "0.2.1"
rustc-hash = "1.1.0"
# lambdas = "0.2.0"
lambdas = { git = "https://github.com/mlb2251/lambdas", rev = "2c9bfd0"}
# lambdas = { git = "https://github.com/mlb2251/lambdas", rev = "2c9bfd0"}
test-case = "3.3.1"


# [patch.crates-io]
# lambdas = { path = "../lambdas"}
lambdas = { path = "../lambdas"}


# enable for flamegraphs
Expand Down
40 changes: 20 additions & 20 deletions src/compression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ impl Pattern {


/// the index of the empty zipper `[]` in the list of zippers
const EMPTY_ZID: ZId = 0;
pub const EMPTY_ZID: ZId = 0;

/// an argument to an abstraction.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -966,13 +966,13 @@ fn stitch_search(

if should_prune_single_task(&shared, &locs) {
if !shared.cfg.no_stats { shared.stats.lock().deref_mut().single_task_fired += 1; }
if tracked && !shared.cfg.quiet { println!("{} single task pruned when expanding {} to {}", "[TRACK]".red().bold(), original_pattern.to_expr(&shared), zipper_replace(original_pattern.to_expr(&shared), &shared.zippers.zip_of_zid[hole_zid], Node::Prim(format!("<{expands_to}>").into()))) }
if tracked && !shared.cfg.quiet { println!("{} single task pruned when expanding {} to {}", "[TRACK]".red().bold(), original_pattern.to_expr(&shared), zipper_replace(original_pattern.to_expr(&shared), &shared.zippers.zip_of_zid[hole_zid], Node::Prim(format!("<{expands_to}>").into()), &shared.zippers)) }
continue 'expansion;
}

// Pruning (FREE VARS): if an invention has free variables in the body then it's not a real function and we can discard it
// Here we just check if our expansion just yielded a variable, and if that is bound based on how many lambdas there are above it.
if expands_to.free_variable(shared.zippers.zip_of_zid[hole_zid].depth_root_to_arg()) {
if expands_to.free_variable(shared.zippers.zip_of_zid[hole_zid].depth_root_to_arg(&shared.zippers)) {
if !shared.cfg.no_stats { shared.stats.lock().deref_mut().free_vars_fired += 1; };
if tracked && !shared.cfg.quiet { println!("{} pruned by free var in body when expanding {} to {}", "[TRACK]".red().bold(), original_pattern.to_expr(&shared), original_pattern.show_track_expansion(hole_zid, &shared)) }
continue 'expansion; // free var
Expand Down Expand Up @@ -1539,7 +1539,7 @@ pub fn inverse_delta(cost_once: Cost, usages: Cost, arg_uses: usize, cost_fn: &E

// (not used in popl code - experimental; always exists at the first return statement unless --inv-arg-cap is turned on)
#[allow(clippy::too_many_arguments)]
pub fn inverse_argument_capture(finished: &mut FinishedPattern, cfg: &CompressionStepConfig, zippers: &Zippers, extensions_of_zid: &[ZIdExtension], set: &ExprSet, analyzed_ivars: &AnalyzedExpr<IVarAnalysis>, cost_fn: &ExprCost) {
pub fn inverse_argument_capture(finished: &mut FinishedPattern, cfg: &CompressionStepConfig, zippers: &mut Zippers, extensions_of_zid: &[ZIdExtension], set: &ExprSet, analyzed_ivars: &AnalyzedExpr<IVarAnalysis>, cost_fn: &ExprCost) {
if !cfg.inv_arg_cap || cfg.no_other_util {
return
}
Expand Down Expand Up @@ -1591,7 +1591,7 @@ fn possible_to_uninline(counts: FxHashMap<Idx, (Cost, Vec<usize>)>, finished_usa
}

/// not used in popl code - experimental
fn use_counts(pattern: &Pattern, zippers: &Zippers, extensions_of_zid: &[ZIdExtension], set: &ExprSet, analyzed_ivars: &AnalyzedExpr<IVarAnalysis>) -> FxHashMap<Idx,(Cost,Vec<ZId>)> {
fn use_counts(pattern: &Pattern, zippers: &mut Zippers, extensions_of_zid: &[ZIdExtension], set: &ExprSet, analyzed_ivars: &AnalyzedExpr<IVarAnalysis>) -> FxHashMap<Idx,(Cost,Vec<ZId>)> {
let mut curr_zip: Zipper = Zipper::default();
let curr_zid: ZId = EMPTY_ZID;
let zids = &pattern.pattern_args.iterate_arguments().cloned().collect::<Vec<LabelledZId>>();
Expand All @@ -1603,14 +1603,14 @@ fn use_counts(pattern: &Pattern, zippers: &Zippers, extensions_of_zid: &[ZIdExte
let mut counts: FxHashMap<Idx,(Cost,Vec<ZId>)> = Default::default();

#[allow(clippy::too_many_arguments)]
fn helper(curr_node: Idx, match_loc: Idx, curr_zip: &mut Zipper, curr_zid: ZId, zips: &[Zipper], zids: &[LabelledZId], arg_of_zid_node: &[FxHashMap<Idx,Arg>], extensions_of_zid: &[ZIdExtension], set: &ExprSet, counts: &mut FxHashMap<Idx,(Cost,Vec<ZId>)>, analyzed_ivars: &AnalyzedExpr<IVarAnalysis>) {
fn helper(curr_node: Idx, match_loc: Idx, curr_zip: &mut Zipper, curr_zid: ZId, zips: &[Zipper], zids: &[LabelledZId], extensions_of_zid: &[ZIdExtension], set: &ExprSet, counts: &mut FxHashMap<Idx,(Cost,Vec<ZId>)>, analyzed_ivars: &AnalyzedExpr<IVarAnalysis>, zippers: &mut Zippers) {
if zids.iter().any(|labelled| labelled.zid == curr_zid){
return // current zip matches an arg
}
// if curr_zip is not a prefix of any arg zipper, then increment its count
if zips.iter().all(|zip| !zip.starts_with(curr_zip)) {
if zips.iter().all(|zip| !zip.starts_with(curr_zip, zippers)) {
// also make sure its valid ie doesnt have any free ivars as ew do during normal checks
let arg = arg_of_zid_node[curr_zid].get(&match_loc).unwrap();
let arg = zippers.arg_of_zid_node[curr_zid].get(&match_loc).unwrap();
if analyzed_ivars[arg.shifted_id].is_empty() {
counts.entry(arg.shifted_id)
.or_insert_with(||(arg.cost as Cost, vec![]))
Expand All @@ -1621,26 +1621,26 @@ fn use_counts(pattern: &Pattern, zippers: &Zippers, extensions_of_zid: &[ZIdExte
Node::Prim(_) => {},
Node::Var(_, _) => {},
Node::Lam(b, _) => {
curr_zip.add_to_end(ZNode::Body);
curr_zip.add_to_end(ZNode::Body, zippers);
let new_zid = extensions_of_zid[curr_zid].body.unwrap();
helper(*b, match_loc, curr_zip, new_zid, zips, zids, arg_of_zid_node, extensions_of_zid, set, counts, analyzed_ivars);
curr_zip.remove_from_end();
helper(*b, match_loc, curr_zip, new_zid, zips, zids, extensions_of_zid, set, counts, analyzed_ivars, zippers);
curr_zip.remove_from_end(zippers);
}
Node::App(f,x) => {
curr_zip.add_to_end(ZNode::Func);
curr_zip.add_to_end(ZNode::Func, zippers);
let new_zid = extensions_of_zid[curr_zid].func.unwrap();
helper(*f, match_loc, curr_zip, new_zid, zips, zids, arg_of_zid_node, extensions_of_zid, set, counts, analyzed_ivars);
curr_zip.remove_from_end();
curr_zip.add_to_end(ZNode::Arg);
helper(*f, match_loc, curr_zip, new_zid, zips, zids, extensions_of_zid, set, counts, analyzed_ivars, zippers);
curr_zip.remove_from_end(zippers);
curr_zip.add_to_end(ZNode::Arg, zippers);
let new_zid = extensions_of_zid[curr_zid].arg.unwrap();
helper(*x, match_loc, curr_zip, new_zid, zips, zids, arg_of_zid_node, extensions_of_zid, set, counts, analyzed_ivars);
curr_zip.remove_from_end();
helper(*x, match_loc, curr_zip, new_zid, zips, zids, extensions_of_zid, set, counts, analyzed_ivars, zippers);
curr_zip.remove_from_end(zippers);
}
_ => unreachable!(),
}
}
// we can pick any match location
helper(pattern.match_locations[0], pattern.match_locations[0], &mut curr_zip, curr_zid, &zips, zids, &zippers.arg_of_zid_node, extensions_of_zid, set, &mut counts, analyzed_ivars);
helper(pattern.match_locations[0], pattern.match_locations[0], &mut curr_zip, curr_zid, &zips, zids, extensions_of_zid, set, &mut counts, analyzed_ivars, zippers);
counts
}

Expand Down Expand Up @@ -1816,7 +1816,7 @@ pub fn construct_shared(
if !cfg.quiet { println!("cost_of_node structs: {:?}ms", tstart.elapsed().as_millis()) }
tstart = std::time::Instant::now();

let (zippers,
let (mut zippers,
zids_of_node,
extensions_of_zid) = get_zippers(&corpus_span, &analyzed_cost, &mut set, &mut analyzed_free_vars);

Expand Down Expand Up @@ -1941,7 +1941,7 @@ pub fn construct_shared(
};

// This handle the case covered by Appendix B in the paper
inverse_argument_capture(&mut finished_pattern, cfg, &zippers, &extensions_of_zid, &set, &analyzed_ivars, cost_fn);
inverse_argument_capture(&mut finished_pattern, cfg, &mut zippers, &extensions_of_zid, &set, &analyzed_ivars, cost_fn);
if !cfg.no_stats { stats.azero_calc_unargcap += 1; };

// Pruning (UPPER BOUND): This is the full upper bound pruning
Expand Down
4 changes: 2 additions & 2 deletions src/expansion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl std::fmt::Display for ExpandsTo {
pub fn tracked_expands_to(pattern: &Pattern, hole_zid: ZId, shared: &SharedData) -> ExpandsTo {
// apply the hole zipper to the original expr being tracked to get the subtree
// this will expand into, then get the ExpandsTo of that
let idx = shared.zippers.zip_of_zid[hole_zid].zip(&shared.tracking.as_ref().unwrap().expr);
let idx = shared.zippers.zip_of_zid[hole_zid].zip(&shared.tracking.as_ref().unwrap().expr, &shared.zippers);
match expands_to_of_node(&shared.tracking.as_ref().unwrap().expr.set[idx]) {
ExpandsTo(ExpandsToInner::IVar(i, VariableType::Metavar)) => {
ExpandsTo(ExpandsToInner::IVar(pattern.pattern_args.find_variable(shared, i as usize) as i32, VariableType::Metavar))
Expand Down Expand Up @@ -176,7 +176,7 @@ pub fn get_ivars_expansions(original_pattern: &Pattern, arg_of_loc: &FxHashMap<I

if shared.cfg.no_curried_metavars {
// dont allow any expansions that result in a metavar to the left of an app
if shared.zippers.zip_of_zid[hole_zid].ends_with_func(){
if shared.zippers.zip_of_zid[hole_zid].ends_with_func(&shared.zippers){
return ivars_expansions;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ pub fn num_paths_to_node(roots: &[Idx], corpus_span: &Span, set: &ExprSet) -> (V
}


pub fn zipper_replace(mut expr: ExprOwned, zipper: &Zipper, new: Node) -> ExprOwned {
let idx = zipper.zip(&expr);
pub fn zipper_replace(mut expr: ExprOwned, zipper: &Zipper, new: Node, zippers: &Zippers) -> ExprOwned {
let idx = zipper.zip(&expr, zippers);
*expr.as_mut().get_node_mut(idx) = new;
expr
}
152 changes: 115 additions & 37 deletions src/zipper.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,125 @@
use lambdas::{AnalyzedExpr, ExprOwned, ExprSet, FreeVarAnalysis, Idx};
use lambdas::{AnalyzedExpr, Expr, ExprOwned, ExprSet, FreeVarAnalysis, Idx};
pub use lambdas::{ZNode, ZId, LabelledZId};
use rustc_hash::FxHashMap;

use crate::{insert_arg_ivars, Arg, Cost, ExpandsTo, ZIdExtension};
use crate::{insert_arg_ivars, Arg, Cost, ExpandsTo, ZIdExtension, EMPTY_ZID};

#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Zipper(Vec<ZNode>);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Zipper {
EmptyZipper,
ConsZipper {
head: ZNode,
tail: ZId,
},
}


impl Zipper {

#[inline(always)]
pub fn ends_with_func(&self) -> bool {
matches!(self.0.last(), Some(ZNode::Func))
pub fn ends_with_func(&self, zippers: &Zippers) -> bool {
return self.function_arity(zippers) > 0
}

#[inline(always)]
pub fn function_arity(&self) -> usize {
self.0.iter().rev().take_while(|znode| **znode == ZNode::Func).count()
pub fn function_arity(&self, zippers: &Zippers) -> usize {
let mut ptr = self;
let mut count = 0;
// looking at the last run of Funcs in the zipper
while let Zipper::ConsZipper { head, tail } = ptr {
match head {
ZNode::Func => count += 1,
_ => count = 0
}
ptr = &zippers.zip_of_zid[*tail];
}
count
}

#[inline(always)]
pub fn depth_root_to_arg(&self) -> usize {
self.0.iter().filter(|x| **x == ZNode::Body).count()
pub fn depth_root_to_arg(&self, zippers: &Zippers) -> usize {
// looking at the first run of Body in the zipper
let mut ptr = self;
let mut count = 0;
while let Zipper::ConsZipper { head: ZNode::Body, tail } = ptr {
count += 1;
ptr = &zippers.zip_of_zid[*tail];
}
count
}

#[inline(always)]
pub fn starts_with(&self, other: &Zipper) -> bool {
self.0.starts_with(&other.0)
pub fn starts_with(&self, other: &Zipper, zippers: &Zippers) -> bool {
// self.0.starts_with(&other.0)
let mut self_ptr = self;
let mut other_ptr = other;
while let (Zipper::ConsZipper { head: self_head, tail: self_tail }, Zipper::ConsZipper { head: other_head, tail: other_tail }) = (self_ptr, other_ptr) {
if self_head != other_head {
return false;
}
self_ptr = &zippers.zip_of_zid[*self_tail];
other_ptr = &zippers.zip_of_zid[*other_tail];
}
// at this point, at least one of the pointers is EmptyZipper, it should be the other one for self to start with the other
other_ptr == &Zipper::EmptyZipper
}

#[inline(always)]
pub fn add_to_front(&mut self, node: ZNode) {
self.0.insert(0, node);
pub fn add_to_end(&mut self, node: ZNode, zippers: &mut Zippers) -> ZId {
match self {
Zipper::EmptyZipper => {
*self = Zipper::ConsZipper { head: node, tail: EMPTY_ZID };
},
Zipper::ConsZipper { head, tail } => {
let mut tail_zip = zippers.zip_of_zid[*tail].clone();
*self = Zipper::ConsZipper { head: head.clone(), tail: tail_zip.add_to_end(node, zippers) };
}
}
zippers.add_zip(self.clone())
}

#[inline(always)]
pub fn add_to_end(&mut self, node: ZNode) {
self.0.push(node);
pub fn remove_from_end(&mut self, zippers: &mut Zippers) -> ZId {
match self {
Zipper::EmptyZipper => {},
Zipper::ConsZipper { head, tail } => {
if *tail == EMPTY_ZID {
*self = Zipper::EmptyZipper;
} else {
let mut tail_zip = zippers.zip_of_zid[*tail].clone();
*self = Zipper::ConsZipper { head: head.clone(), tail: tail_zip.remove_from_end(zippers) };
}
}
}
zippers.add_zip(self.clone())
}

#[inline(always)]
pub fn remove_from_end(&mut self) {
self.0.pop();
pub fn zip(&self, expr: &ExprOwned, zippers: &Zippers) -> ZId {

self._zip(expr.immut(), zippers).idx
}

#[inline(always)]
pub fn zip(&self, expr: &ExprOwned) -> ZId {
expr.immut().zip(&self.0).idx
pub fn _zip<'a>(&self, expr: Expr<'a>, zippers: &Zippers) -> Expr<'a> {
match self {
Zipper::EmptyZipper => expr,
Zipper::ConsZipper { head, tail } => {
// let head_vec = vec![head.clone()];
let expr = expr.zip_once(head.clone());
let expr = zippers.zip_of_zid[*tail]._zip(expr, zippers);
expr
}
}
}

}

impl Default for Zipper {
fn default() -> Self {
Zipper::EmptyZipper
}
}

#[derive(Clone, Debug, Default)]
pub struct Zippers {
pub zid_of_zip: FxHashMap<Zipper, ZId>,
Expand Down Expand Up @@ -82,8 +150,20 @@ impl Zippers {

#[inline(always)]
pub fn extend_zipper(&mut self, unextended_zid: ZId, extended_node: Idx, unextended_node: Idx, znode: ZNode) -> usize {
let mut zip = self.zip_of_zid[unextended_zid].clone();
zip.add_to_front(znode);
let zip = Zipper::ConsZipper {
head: znode,
tail: unextended_zid,
};
let zid = self.add_zip(zip);
// add new zid to this node
// give it the same arg
let arg = self.arg_of_zid_node[unextended_zid][&unextended_node].clone();
self.arg_of_zid_node[zid].insert(extended_node, arg);
zid
}

#[inline(always)]
fn add_zip(&mut self, zip: Zipper) -> usize {
let zip_of_zid = &mut self.zip_of_zid;
let arg_of_zid_node = &mut self.arg_of_zid_node;
let zid = self.zid_of_zip.entry(zip.clone()).or_insert_with(|| {
Expand All @@ -92,13 +172,9 @@ impl Zippers {
arg_of_zid_node.push(FxHashMap::default());
zid
});
// add new zid to this node
// give it the same arg
let arg = self.arg_of_zid_node[unextended_zid][&unextended_node].clone();
self.arg_of_zid_node[*zid].insert(extended_node, arg);
*zid
}

#[inline(always)]
pub fn handle_shift(&mut self, extended_zid: ZId, unextended_zid: ZId, extended_node: Idx, unextended_node: Idx, analyzed_free_vars: &mut AnalyzedExpr<FreeVarAnalysis>, set: &mut ExprSet) {
let zip = &self.zip_of_zid[extended_zid];
Expand All @@ -111,7 +187,7 @@ impl Zippers {
// by inserting an IVar to indicate this

// how many lambdas are along this zipper? (including most recent one)
let depth_root_to_arg = zip.depth_root_to_arg() as i32;
let depth_root_to_arg = zip.depth_root_to_arg(self) as i32;

// find all pointers to $0 (this is the `init_depth` parameter) and replace then with #(num_lams - 1) that is
// point past all lambdas except the newly added one. For example if there were no lambdas other than the
Expand All @@ -125,20 +201,22 @@ impl Zippers {
}

#[inline(always)]
pub fn compute_extensions(&self) -> Vec<ZIdExtension> {
self.zip_of_zid.iter().map(|zip| {
pub fn compute_extensions(&mut self) -> Vec<ZIdExtension> {
let mut extensions = vec![];
for zip in self.zip_of_zid.clone().into_iter() {
let mut zip_body = zip.clone();
zip_body.add_to_end(ZNode::Body);
zip_body.add_to_end(ZNode::Body, self);
let mut zip_arg = zip.clone();
zip_arg.add_to_end(ZNode::Arg);
zip_arg.add_to_end(ZNode::Arg, self);
let mut zip_func = zip.clone();
zip_func.add_to_end(ZNode::Func);
ZIdExtension {
zip_func.add_to_end(ZNode::Func, self);
extensions.push(ZIdExtension {
body: self.zid_of_zip.get(&zip_body).copied(),
arg: self.zid_of_zip.get(&zip_arg).copied(),
func: self.zid_of_zip.get(&zip_func).copied(),
}
}).collect()
});
}
extensions
}

pub fn print_stats(&self) {
Expand Down
Loading