optimize Path

This commit is contained in:
Folkert 2021-03-22 13:09:56 +01:00
parent fdebfa6b3f
commit addf130be5

View file

@ -22,7 +22,7 @@ pub fn compile<'a>(raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>) -> Decision
.into_iter() .into_iter()
.map(|(guard, pattern, index)| Branch { .map(|(guard, pattern, index)| Branch {
goal: index, goal: index,
patterns: vec![(Path::Empty, guard, pattern)], patterns: vec![(Vec::new(), guard, pattern)],
}) })
.collect(); .collect();
@ -52,7 +52,7 @@ impl<'a> Guard<'a> {
pub enum DecisionTree<'a> { pub enum DecisionTree<'a> {
Match(Label), Match(Label),
Decision { Decision {
path: Path, path: Vec<PathInstruction>,
edges: Vec<(Test<'a>, DecisionTree<'a>)>, edges: Vec<(Test<'a>, DecisionTree<'a>)>,
default: Option<Box<DecisionTree<'a>>>, default: Option<Box<DecisionTree<'a>>>,
}, },
@ -148,7 +148,7 @@ pub enum Path {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
struct Branch<'a> { struct Branch<'a> {
goal: Label, goal: Label,
patterns: Vec<(Path, Guard<'a>, Pattern<'a>)>, patterns: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
} }
fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree { fn to_decision_tree(raw_branches: Vec<Branch>) -> DecisionTree {
@ -218,8 +218,8 @@ fn flatten_patterns(branch: Branch) -> Branch {
} }
fn flatten<'a>( fn flatten<'a>(
path_pattern: (Path, Guard<'a>, Pattern<'a>), path_pattern: (Vec<PathInstruction>, Guard<'a>, Pattern<'a>),
path_patterns: &mut Vec<(Path, Guard<'a>, Pattern<'a>)>, path_patterns: &mut Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
) { ) {
match path_pattern.2 { match path_pattern.2 {
Pattern::AppliedTag { Pattern::AppliedTag {
@ -237,12 +237,13 @@ fn flatten<'a>(
{ {
// TODO ^ do we need to check that guard.is_none() here? // TODO ^ do we need to check that guard.is_none() here?
let path = path_pattern.0; let mut path = path_pattern.0;
// Theory: unbox doesn't have any value for us, because one-element tag unions // Theory: unbox doesn't have any value for us, because one-element tag unions
// don't store the tag anyway. // don't store the tag anyway.
if arguments.len() == 1 { if arguments.len() == 1 {
// NOTE here elm will unbox, but we don't use that
path_patterns.push(( path_patterns.push((
Path::Unbox(Box::new(path)), path,
path_pattern.1.clone(), path_pattern.1.clone(),
Pattern::AppliedTag { Pattern::AppliedTag {
union, union,
@ -254,13 +255,15 @@ fn flatten<'a>(
)); ));
} else { } else {
for (index, (arg_pattern, _)) in arguments.iter().enumerate() { for (index, (arg_pattern, _)) in arguments.iter().enumerate() {
let mut new_path = path.clone();
new_path.push(PathInstruction {
index: index as u64,
tag_id,
});
flatten( flatten(
( (
Path::Index { new_path,
index: index as u64,
tag_id,
path: Box::new(path.clone()),
},
// same guard here? // same guard here?
path_pattern.1.clone(), path_pattern.1.clone(),
arg_pattern.clone(), arg_pattern.clone(),
@ -300,7 +303,7 @@ fn check_for_match(branches: &Vec<Branch>) -> Option<Label> {
fn gather_edges<'a>( fn gather_edges<'a>(
branches: Vec<Branch<'a>>, branches: Vec<Branch<'a>>,
path: &Path, path: &Vec<PathInstruction>,
) -> (Vec<(Test<'a>, Vec<Branch<'a>>)>, Vec<Branch<'a>>) { ) -> (Vec<(Test<'a>, Vec<Branch<'a>>)>, Vec<Branch<'a>>) {
let relevant_tests = tests_at_path(path, &branches); let relevant_tests = tests_at_path(path, &branches);
@ -326,7 +329,10 @@ fn gather_edges<'a>(
/// FIND RELEVANT TESTS /// FIND RELEVANT TESTS
fn tests_at_path<'a>(selected_path: &Path, branches: &[Branch<'a>]) -> Vec<Test<'a>> { fn tests_at_path<'a>(
selected_path: &Vec<PathInstruction>,
branches: &[Branch<'a>],
) -> Vec<Test<'a>> {
// NOTE the ordering of the result is important! // NOTE the ordering of the result is important!
let mut all_tests = Vec::new(); let mut all_tests = Vec::new();
@ -360,7 +366,11 @@ fn tests_at_path<'a>(selected_path: &Path, branches: &[Branch<'a>]) -> Vec<Test<
unique unique
} }
fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut Vec<Test<'a>>) { fn test_at_path<'a>(
selected_path: &Vec<PathInstruction>,
branch: &Branch<'a>,
all_tests: &mut Vec<Test<'a>>,
) {
use Pattern::*; use Pattern::*;
use Test::*; use Test::*;
@ -469,7 +479,7 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
/// BUILD EDGES /// BUILD EDGES
fn edges_for<'a>( fn edges_for<'a>(
path: &Path, path: &Vec<PathInstruction>,
branches: Vec<Branch<'a>>, branches: Vec<Branch<'a>>,
test: Test<'a>, test: Test<'a>,
) -> (Test<'a>, Vec<Branch<'a>>) { ) -> (Test<'a>, Vec<Branch<'a>>) {
@ -484,7 +494,7 @@ fn edges_for<'a>(
fn to_relevant_branch<'a>( fn to_relevant_branch<'a>(
test: &Test<'a>, test: &Test<'a>,
path: &Path, path: &Vec<PathInstruction>,
branch: &Branch<'a>, branch: &Branch<'a>,
new_branches: &mut Vec<Branch<'a>>, new_branches: &mut Vec<Branch<'a>>,
) { ) {
@ -524,9 +534,9 @@ fn to_relevant_branch<'a>(
fn to_relevant_branch_help<'a>( fn to_relevant_branch_help<'a>(
test: &Test<'a>, test: &Test<'a>,
path: &Path, path: &Vec<PathInstruction>,
mut start: Vec<(Path, Guard<'a>, Pattern<'a>)>, mut start: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
end: Vec<(Path, Guard<'a>, Pattern<'a>)>, end: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
branch: &Branch<'a>, branch: &Branch<'a>,
guard: Guard<'a>, guard: Guard<'a>,
pattern: Pattern<'a>, pattern: Pattern<'a>,
@ -550,15 +560,13 @@ fn to_relevant_branch_help<'a>(
DestructType::Required(_) => Pattern::Underscore, DestructType::Required(_) => Pattern::Underscore,
}; };
( let mut new_path = path.clone();
Path::Index { new_path.push(PathInstruction {
index: index as u64, index: index as u64,
tag_id: *tag_id, tag_id: *tag_id,
path: Box::new(path.clone()), });
},
Guard::NoGuard, (new_path, Guard::NoGuard, pattern)
pattern,
)
}); });
start.extend(sub_positions); start.extend(sub_positions);
start.extend(end); start.extend(end);
@ -597,26 +605,21 @@ fn to_relevant_branch_help<'a>(
debug_assert_eq!(arguments.len(), 1); debug_assert_eq!(arguments.len(), 1);
let arg = arguments[0].clone(); let arg = arguments[0].clone();
{ {
start.push(( // NOTE here elm unboxes, but we ignore that
Path::Unbox(Box::new(path.clone())), // Path::Unbox(Box::new(path.clone()))
guard, start.push((path.clone(), guard, arg.0));
arg.0,
));
start.extend(end); start.extend(end);
} }
} }
Wrapped::RecordOrSingleTagUnion => { Wrapped::RecordOrSingleTagUnion => {
let sub_positions = arguments.into_iter().enumerate().map( let sub_positions = arguments.into_iter().enumerate().map(
|(index, (pattern, _))| { |(index, (pattern, _))| {
( let mut new_path = path.clone();
Path::Index { new_path.push(PathInstruction {
index: index as u64, index: index as u64,
tag_id, tag_id,
path: Box::new(path.clone()), });
}, (new_path, Guard::NoGuard, pattern)
Guard::NoGuard,
pattern,
)
}, },
); );
start.extend(sub_positions); start.extend(sub_positions);
@ -625,15 +628,12 @@ fn to_relevant_branch_help<'a>(
Wrapped::MultiTagUnion => { Wrapped::MultiTagUnion => {
let sub_positions = arguments.into_iter().enumerate().map( let sub_positions = arguments.into_iter().enumerate().map(
|(index, (pattern, _))| { |(index, (pattern, _))| {
( let mut new_path = path.clone();
Path::Index { new_path.push(PathInstruction {
index: 1 + index as u64, index: 1 + index as u64,
tag_id, tag_id,
path: Box::new(path.clone()), });
}, (new_path, Guard::NoGuard, pattern)
Guard::NoGuard,
pattern,
)
}, },
); );
start.extend(sub_positions); start.extend(sub_positions);
@ -715,15 +715,15 @@ fn to_relevant_branch_help<'a>(
enum Extract<'a> { enum Extract<'a> {
NotFound, NotFound,
Found { Found {
start: Vec<(Path, Guard<'a>, Pattern<'a>)>, start: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
found_pattern: (Guard<'a>, Pattern<'a>), found_pattern: (Guard<'a>, Pattern<'a>),
end: Vec<(Path, Guard<'a>, Pattern<'a>)>, end: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
}, },
} }
fn extract<'a>( fn extract<'a>(
selected_path: &Path, selected_path: &Vec<PathInstruction>,
path_patterns: Vec<(Path, Guard<'a>, Pattern<'a>)>, path_patterns: Vec<(Vec<PathInstruction>, Guard<'a>, Pattern<'a>)>,
) -> Extract<'a> { ) -> Extract<'a> {
let mut start = Vec::new(); let mut start = Vec::new();
@ -746,7 +746,7 @@ fn extract<'a>(
/// FIND IRRELEVANT BRANCHES /// FIND IRRELEVANT BRANCHES
fn is_irrelevant_to<'a>(selected_path: &Path, branch: &Branch<'a>) -> bool { fn is_irrelevant_to<'a>(selected_path: &Vec<PathInstruction>, branch: &Branch<'a>) -> bool {
match branch match branch
.patterns .patterns
.iter() .iter()
@ -775,7 +775,7 @@ fn needs_tests(pattern: &Pattern) -> bool {
/// PICK A PATH /// PICK A PATH
fn pick_path<'a>(branches: &'a [Branch]) -> &'a Path { fn pick_path<'a>(branches: &'a [Branch]) -> &'a Vec<PathInstruction> {
let mut all_paths = Vec::with_capacity(branches.len()); let mut all_paths = Vec::with_capacity(branches.len());
// is choice path // is choice path
@ -804,9 +804,12 @@ fn pick_path<'a>(branches: &'a [Branch]) -> &'a Path {
} }
} }
fn bests_by_small_branching_factor<'a, I>(branches: &[Branch], mut all_paths: I) -> Vec<&'a Path> fn bests_by_small_branching_factor<'a, I>(
branches: &[Branch],
mut all_paths: I,
) -> Vec<&'a Vec<PathInstruction>>
where where
I: Iterator<Item = &'a Path>, I: Iterator<Item = &'a Vec<PathInstruction>>,
{ {
match all_paths.next() { match all_paths.next() {
None => panic!("Cannot choose the best of zero paths. This should never happen."), None => panic!("Cannot choose the best of zero paths. This should never happen."),
@ -836,9 +839,12 @@ where
} }
} }
fn bests_by_small_defaults<'a, I>(branches: &[Branch], mut all_paths: I) -> Vec<&'a Path> fn bests_by_small_defaults<'a, I>(
branches: &[Branch],
mut all_paths: I,
) -> Vec<&'a Vec<PathInstruction>>
where where
I: Iterator<Item = &'a Path>, I: Iterator<Item = &'a Vec<PathInstruction>>,
{ {
match all_paths.next() { match all_paths.next() {
None => panic!("Cannot choose the best of zero paths. This should never happen."), None => panic!("Cannot choose the best of zero paths. This should never happen."),
@ -870,7 +876,7 @@ where
/// PATH PICKING HEURISTICS /// PATH PICKING HEURISTICS
fn small_defaults(branches: &[Branch], path: &Path) -> usize { fn small_defaults(branches: &[Branch], path: &Vec<PathInstruction>) -> usize {
branches branches
.iter() .iter()
.filter(|b| is_irrelevant_to(path, b)) .filter(|b| is_irrelevant_to(path, b))
@ -878,7 +884,7 @@ fn small_defaults(branches: &[Branch], path: &Path) -> usize {
.sum() .sum()
} }
fn small_branching_factor(branches: &[Branch], path: &Path) -> usize { fn small_branching_factor(branches: &[Branch], path: &Vec<PathInstruction>) -> usize {
let (edges, fallback) = gather_edges(branches.to_vec(), path); let (edges, fallback) = gather_edges(branches.to_vec(), path);
edges.len() + (if fallback.is_empty() { 0 } else { 1 }) edges.len() + (if fallback.is_empty() { 0 } else { 1 })
@ -888,12 +894,12 @@ fn small_branching_factor(branches: &[Branch], path: &Path) -> usize {
enum Decider<'a, T> { enum Decider<'a, T> {
Leaf(T), Leaf(T),
Chain { Chain {
test_chain: Vec<(Path, Test<'a>)>, test_chain: Vec<(Vec<PathInstruction>, Test<'a>)>,
success: Box<Decider<'a, T>>, success: Box<Decider<'a, T>>,
failure: Box<Decider<'a, T>>, failure: Box<Decider<'a, T>>,
}, },
FanOut { FanOut {
path: Path, path: Vec<PathInstruction>,
tests: Vec<(Test<'a>, Decider<'a, T>)>, tests: Vec<(Test<'a>, Decider<'a, T>)>,
fallback: Box<Decider<'a, T>>, fallback: Box<Decider<'a, T>>,
}, },
@ -972,8 +978,8 @@ pub fn optimize_when<'a>(
stmt stmt
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct PathInstruction { pub struct PathInstruction {
index: u64, index: u64,
tag_id: u8, tag_id: u8,
} }
@ -1009,12 +1015,13 @@ fn reverse_path(mut path: &Path) -> Vec<PathInstruction> {
fn path_to_expr_help<'a>( fn path_to_expr_help<'a>(
env: &mut Env<'a, '_>, env: &mut Env<'a, '_>,
mut symbol: Symbol, mut symbol: Symbol,
path: &Path, path: &Vec<PathInstruction>,
mut layout: Layout<'a>, mut layout: Layout<'a>,
) -> (Symbol, StoresVec<'a>, Layout<'a>) { ) -> (Symbol, StoresVec<'a>, Layout<'a>) {
let mut stores = bumpalo::collections::Vec::new_in(env.arena); let mut stores = bumpalo::collections::Vec::new_in(env.arena);
let instructions = reverse_path(path); // let instructions = reverse_path(path);
let instructions = path;
let mut it = instructions.iter().peekable(); let mut it = instructions.iter().peekable();
while let Some(PathInstruction { index, tag_id }) = it.next() { while let Some(PathInstruction { index, tag_id }) = it.next() {
@ -1134,7 +1141,7 @@ fn test_to_equality<'a>(
env: &mut Env<'a, '_>, env: &mut Env<'a, '_>,
cond_symbol: Symbol, cond_symbol: Symbol,
cond_layout: &Layout<'a>, cond_layout: &Layout<'a>,
path: &Path, path: &Vec<PathInstruction>,
test: Test<'a>, test: Test<'a>,
) -> (StoresVec<'a>, Symbol, Symbol, Layout<'a>) { ) -> (StoresVec<'a>, Symbol, Symbol, Layout<'a>) {
let (rhs_symbol, mut stores, _layout) = let (rhs_symbol, mut stores, _layout) =
@ -1272,7 +1279,7 @@ fn stores_and_condition<'a>(
env: &mut Env<'a, '_>, env: &mut Env<'a, '_>,
cond_symbol: Symbol, cond_symbol: Symbol,
cond_layout: &Layout<'a>, cond_layout: &Layout<'a>,
test_chain: Vec<(Path, Test<'a>)>, test_chain: Vec<(Vec<PathInstruction>, Test<'a>)>,
) -> (Tests<'a>, Option<(Symbol, JoinPointId, Stmt<'a>)>) { ) -> (Tests<'a>, Option<(Symbol, JoinPointId, Stmt<'a>)>) {
let mut tests = Vec::with_capacity(test_chain.len()); let mut tests = Vec::with_capacity(test_chain.len());
@ -1500,11 +1507,11 @@ impl<'a> ConstructorKnown<'a> {
fn from_test_chain( fn from_test_chain(
cond_symbol: Symbol, cond_symbol: Symbol,
cond_layout: &Layout<'a>, cond_layout: &Layout<'a>,
test_chain: &[(Path, Test)], test_chain: &[(Vec<PathInstruction>, Test)],
) -> Self { ) -> Self {
match test_chain { match test_chain {
[(path, test)] => match (path, test) { [(path, test)] => match test {
(Path::Empty, Test::IsCtor { tag_id, union, .. }) => { Test::IsCtor { tag_id, union, .. } if path.is_empty() => {
if union.alternatives.len() == 2 { if union.alternatives.len() == 2 {
// excluded middle: we also know the tag_id in the fail branch // excluded middle: we also know the tag_id in the fail branch
ConstructorKnown::Both { ConstructorKnown::Both {
@ -1846,7 +1853,7 @@ fn tree_to_decider(tree: DecisionTree) -> Decider<u64> {
} }
fn to_chain<'a>( fn to_chain<'a>(
path: Path, path: Vec<PathInstruction>,
test: Test<'a>, test: Test<'a>,
success_tree: DecisionTree<'a>, success_tree: DecisionTree<'a>,
failure_tree: DecisionTree<'a>, failure_tree: DecisionTree<'a>,